mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-18 18:17:03 +00:00
Merge branch 'feature/whitelist-51', closes #51
This commit is contained in:
@@ -18,6 +18,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- RedHat `.rpm` package generation to release process.
|
- RedHat `.rpm` package generation to release process.
|
||||||
- Message seen flag in REST and Web UI so you can see which messages have
|
- Message seen flag in REST and Web UI so you can see which messages have
|
||||||
already been read.
|
already been read.
|
||||||
|
- Recipient domain accept policy; Inbucket can now reject mail to specific
|
||||||
|
domains.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Massive refactor of back-end code. Inbucket should now be both easier and
|
- Massive refactor of back-end code. Inbucket should now be both easier and
|
||||||
@@ -34,9 +36,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
flag; which is enabled by default for the Docker container.
|
flag; which is enabled by default for the Docker container.
|
||||||
- SMTP and POP3 network tracing is no longer logged regardless of level, but can
|
- SMTP and POP3 network tracing is no longer logged regardless of level, but can
|
||||||
be sent to stdout via `-netdebug` flag.
|
be sent to stdout via `-netdebug` flag.
|
||||||
|
- Replaced store/nostore config variables with a storage policy that mirrors the
|
||||||
|
domain accept policy.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Support for SIGHUP and log file rotation.
|
- No longer support SIGHUP or log file rotation.
|
||||||
|
|
||||||
|
|
||||||
## [v1.3.1] - 2018-03-10
|
## [v1.3.1] - 2018-03-10
|
||||||
|
|||||||
@@ -11,10 +11,14 @@ variables it supports:
|
|||||||
INBUCKET_LOGLEVEL INFO DEBUG, INFO, WARN, or ERROR
|
INBUCKET_LOGLEVEL INFO DEBUG, INFO, WARN, or ERROR
|
||||||
INBUCKET_SMTP_ADDR 0.0.0.0:2500 SMTP server IP4 host:port
|
INBUCKET_SMTP_ADDR 0.0.0.0:2500 SMTP server IP4 host:port
|
||||||
INBUCKET_SMTP_DOMAIN inbucket HELO domain
|
INBUCKET_SMTP_DOMAIN inbucket HELO domain
|
||||||
INBUCKET_SMTP_DOMAINNOSTORE Load testing domain
|
|
||||||
INBUCKET_SMTP_MAXRECIPIENTS 200 Maximum RCPT TO per message
|
INBUCKET_SMTP_MAXRECIPIENTS 200 Maximum RCPT TO per message
|
||||||
INBUCKET_SMTP_MAXMESSAGEBYTES 10240000 Maximum message size
|
INBUCKET_SMTP_MAXMESSAGEBYTES 10240000 Maximum message size
|
||||||
INBUCKET_SMTP_STOREMESSAGES true Store incoming mail?
|
INBUCKET_SMTP_DEFAULTACCEPT true Accept all mail by default?
|
||||||
|
INBUCKET_SMTP_ACCEPTDOMAINS Domains to accept mail for
|
||||||
|
INBUCKET_SMTP_REJECTDOMAINS Domains to reject mail for
|
||||||
|
INBUCKET_SMTP_DEFAULTSTORE true Store all mail by default?
|
||||||
|
INBUCKET_SMTP_STOREDOMAINS Domains to store mail for
|
||||||
|
INBUCKET_SMTP_DISCARDDOMAINS Domains to discard mail for
|
||||||
INBUCKET_SMTP_TIMEOUT 300s Idle network timeout
|
INBUCKET_SMTP_TIMEOUT 300s Idle network timeout
|
||||||
INBUCKET_POP3_ADDR 0.0.0.0:1100 POP3 server IP4 host:port
|
INBUCKET_POP3_ADDR 0.0.0.0:1100 POP3 server IP4 host:port
|
||||||
INBUCKET_POP3_DOMAIN inbucket HELLO domain
|
INBUCKET_POP3_DOMAIN inbucket HELLO domain
|
||||||
@@ -75,17 +79,6 @@ Most SMTP clients appear to ignore this value.
|
|||||||
|
|
||||||
- Default: `inbucket`
|
- Default: `inbucket`
|
||||||
|
|
||||||
### Load Testing/No Store Domain
|
|
||||||
|
|
||||||
`INBUCKET_SMTP_DOMAINNOSTORE`
|
|
||||||
|
|
||||||
Mail sent to this domain will not be stored by Inbucket. This is helpful if you
|
|
||||||
are load or soak testing a service, and do not plan to inspect the resulting
|
|
||||||
emails. Messages sent to a domain other than this will be stored normally.
|
|
||||||
|
|
||||||
- Default: None
|
|
||||||
- Example: `bitbucket.local`
|
|
||||||
|
|
||||||
### Maximum Recipients
|
### Maximum Recipients
|
||||||
|
|
||||||
`INBUCKET_SMTP_MAXRECIPIENTS`
|
`INBUCKET_SMTP_MAXRECIPIENTS`
|
||||||
@@ -105,17 +98,74 @@ exceeding this size will be rejected during the SMTP `DATA` phase.
|
|||||||
|
|
||||||
- Default: `10240000` (10MB)
|
- Default: `10240000` (10MB)
|
||||||
|
|
||||||
### Store Messages
|
### Default Recipient Accept Policy
|
||||||
|
|
||||||
`INBUCKET_SMTP_STOREMESSAGES`
|
`INBUCKET_SMTP_DEFAULTACCEPT`
|
||||||
|
|
||||||
This option can be used to disable mail storage entirely. Useful for load
|
If true, Inbucket will accept mail to any domain unless present in the reject
|
||||||
testing, or turning Inbucket into a black hole that will consume our entire
|
domains list. If false, recipients will be rejected unless their domain is
|
||||||
solar system.
|
present in the accept domains list.
|
||||||
|
|
||||||
- Default: `true`
|
- Default: `true`
|
||||||
- Values: `true` or `false`
|
- Values: `true` or `false`
|
||||||
|
|
||||||
|
### Accepted Recipient Domain List
|
||||||
|
|
||||||
|
`INBUCKET_SMTP_ACCEPTDOMAINS`
|
||||||
|
|
||||||
|
List of domains to accept mail for when `INBUCKET_SMTP_DEFAULTACCEPT` is false;
|
||||||
|
has no effect when true.
|
||||||
|
|
||||||
|
- Default: None
|
||||||
|
- Values: Comma separated list of domains
|
||||||
|
- Example: `localhost,mysite.org`
|
||||||
|
|
||||||
|
### Rejected Recipient Domain List
|
||||||
|
|
||||||
|
`INBUCKET_SMTP_REJECTDOMAINS`
|
||||||
|
|
||||||
|
List of domains to reject mail for when `INBUCKET_SMTP_DEFAULTACCEPT` is true;
|
||||||
|
has no effect when false.
|
||||||
|
|
||||||
|
- Default: None
|
||||||
|
- Values: Comma separated list of domains
|
||||||
|
- Example: `reject.com,gmail.com`
|
||||||
|
|
||||||
|
### Default Recipient Store Policy
|
||||||
|
|
||||||
|
`INBUCKET_SMTP_DEFAULTSTORE`
|
||||||
|
|
||||||
|
If true, Inbucket will store mail sent to any domain unless present in the
|
||||||
|
discard domains list. If false, messages will be discarded unless their domain
|
||||||
|
is present in the store domains list.
|
||||||
|
|
||||||
|
- Default: `true`
|
||||||
|
- Values: `true` or `false`
|
||||||
|
|
||||||
|
### Stored Recipient Domain List
|
||||||
|
|
||||||
|
`INBUCKET_SMTP_STOREDOMAINS`
|
||||||
|
|
||||||
|
List of domains to store mail for when `INBUCKET_SMTP_DEFAULTSTORE` is false;
|
||||||
|
has no effect when true.
|
||||||
|
|
||||||
|
- Default: None
|
||||||
|
- Values: Comma separated list of domains
|
||||||
|
- Example: `localhost,mysite.org`
|
||||||
|
|
||||||
|
### Discarded Recipient Domain List
|
||||||
|
|
||||||
|
`INBUCKET_SMTP_DISCARDDOMAINS`
|
||||||
|
|
||||||
|
Mail sent to these domains will not be stored by Inbucket. This is helpful if
|
||||||
|
you are load or soak testing a service, and do not plan to inspect the resulting
|
||||||
|
emails. Messages sent to a domain other than this will be stored normally.
|
||||||
|
Only has an effect when `INBUCKET_SMTP_DEFAULTSTORE` is true.
|
||||||
|
|
||||||
|
- Default: None
|
||||||
|
- Values: Comma separated list of domains
|
||||||
|
- Example: `recycle.com,loadtest.org`
|
||||||
|
|
||||||
### Network Idle Timeout
|
### Network Idle Timeout
|
||||||
|
|
||||||
`INBUCKET_SMTP_TIMEOUT`
|
`INBUCKET_SMTP_TIMEOUT`
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package config
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/jhillyerd/inbucket/pkg/stringutil"
|
||||||
"github.com/kelseyhightower/envconfig"
|
"github.com/kelseyhightower/envconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,10 +41,14 @@ type Root struct {
|
|||||||
type SMTP struct {
|
type SMTP struct {
|
||||||
Addr string `required:"true" default:"0.0.0.0:2500" desc:"SMTP server IP4 host:port"`
|
Addr string `required:"true" default:"0.0.0.0:2500" desc:"SMTP server IP4 host:port"`
|
||||||
Domain string `required:"true" default:"inbucket" desc:"HELO domain"`
|
Domain string `required:"true" default:"inbucket" desc:"HELO domain"`
|
||||||
DomainNoStore string `desc:"Load testing domain"`
|
|
||||||
MaxRecipients int `required:"true" default:"200" desc:"Maximum RCPT TO per message"`
|
MaxRecipients int `required:"true" default:"200" desc:"Maximum RCPT TO per message"`
|
||||||
MaxMessageBytes int `required:"true" default:"10240000" desc:"Maximum message size"`
|
MaxMessageBytes int `required:"true" default:"10240000" desc:"Maximum message size"`
|
||||||
StoreMessages bool `required:"true" default:"true" desc:"Store incoming mail?"`
|
DefaultAccept bool `required:"true" default:"true" desc:"Accept all mail by default?"`
|
||||||
|
AcceptDomains []string `desc:"Domains to accept mail for"`
|
||||||
|
RejectDomains []string `desc:"Domains to reject mail for"`
|
||||||
|
DefaultStore bool `required:"true" default:"true" desc:"Store all mail by default?"`
|
||||||
|
StoreDomains []string `desc:"Domains to store mail for"`
|
||||||
|
DiscardDomains []string `desc:"Domains to discard mail for"`
|
||||||
Timeout time.Duration `required:"true" default:"300s" desc:"Idle network timeout"`
|
Timeout time.Duration `required:"true" default:"300s" desc:"Idle network timeout"`
|
||||||
Debug bool `ignored:"true"`
|
Debug bool `ignored:"true"`
|
||||||
}
|
}
|
||||||
@@ -82,7 +86,10 @@ type Storage struct {
|
|||||||
func Process() (*Root, error) {
|
func Process() (*Root, error) {
|
||||||
c := &Root{}
|
c := &Root{}
|
||||||
err := envconfig.Process(prefix, c)
|
err := envconfig.Process(prefix, c)
|
||||||
c.SMTP.DomainNoStore = strings.ToLower(c.SMTP.DomainNoStore)
|
stringutil.SliceToLower(c.SMTP.AcceptDomains)
|
||||||
|
stringutil.SliceToLower(c.SMTP.RejectDomains)
|
||||||
|
stringutil.SliceToLower(c.SMTP.StoreDomains)
|
||||||
|
stringutil.SliceToLower(c.SMTP.DiscardDomains)
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jhillyerd/inbucket/pkg/config"
|
"github.com/jhillyerd/inbucket/pkg/config"
|
||||||
|
"github.com/jhillyerd/inbucket/pkg/stringutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Addressing handles email address policy.
|
// Addressing handles email address policy.
|
||||||
@@ -30,17 +31,33 @@ func (a *Addressing) NewRecipient(address string) (*Recipient, error) {
|
|||||||
}
|
}
|
||||||
return &Recipient{
|
return &Recipient{
|
||||||
Address: *ar,
|
Address: *ar,
|
||||||
apolicy: a,
|
addrPolicy: a,
|
||||||
LocalPart: local,
|
LocalPart: local,
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
Mailbox: mailbox,
|
Mailbox: mailbox,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldStoreDomain indicates if Inbucket stores email destined for the specified domain.
|
// ShouldAcceptDomain indicates if Inbucket accepts mail destined for the specified domain.
|
||||||
|
func (a *Addressing) ShouldAcceptDomain(domain string) bool {
|
||||||
|
domain = strings.ToLower(domain)
|
||||||
|
if a.Config.DefaultAccept && !stringutil.SliceContains(a.Config.RejectDomains, domain) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !a.Config.DefaultAccept && stringutil.SliceContains(a.Config.AcceptDomains, domain) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldStoreDomain indicates if Inbucket stores mail destined for the specified domain.
|
||||||
func (a *Addressing) ShouldStoreDomain(domain string) bool {
|
func (a *Addressing) ShouldStoreDomain(domain string) bool {
|
||||||
if a.Config.StoreMessages {
|
domain = strings.ToLower(domain)
|
||||||
return strings.ToLower(domain) != strings.ToLower(a.Config.DomainNoStore)
|
if a.Config.DefaultStore && !stringutil.SliceContains(a.Config.DiscardDomains, domain) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !a.Config.DefaultStore && stringutil.SliceContains(a.Config.StoreDomains, domain) {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
"github.com/jhillyerd/inbucket/pkg/policy"
|
"github.com/jhillyerd/inbucket/pkg/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShouldStoreDomain(t *testing.T) {
|
func TestShouldAcceptDomain(t *testing.T) {
|
||||||
// Test with storage enabled.
|
// Test with default accept.
|
||||||
ap := &policy.Addressing{
|
ap := &policy.Addressing{
|
||||||
Config: config.SMTP{
|
Config: config.SMTP{
|
||||||
DomainNoStore: "Foo.Com",
|
DefaultAccept: true,
|
||||||
StoreMessages: true,
|
RejectDomains: []string{"a.deny.com", "deny.com"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
@@ -21,23 +21,24 @@ func TestShouldStoreDomain(t *testing.T) {
|
|||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{domain: "bar.com", want: true},
|
{domain: "bar.com", want: true},
|
||||||
{domain: "foo.com", want: false},
|
{domain: "DENY.com", want: false},
|
||||||
{domain: "FOO.com", want: false},
|
{domain: "a.deny.com", want: false},
|
||||||
{domain: "bar.foo.com", want: true},
|
{domain: "b.deny.com", want: true},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.domain, func(t *testing.T) {
|
t.Run(tc.domain, func(t *testing.T) {
|
||||||
got := ap.ShouldStoreDomain(tc.domain)
|
got := ap.ShouldAcceptDomain(tc.domain)
|
||||||
if got != tc.want {
|
if got != tc.want {
|
||||||
t.Errorf("Got %v for %q, want: %v", got, tc.domain, tc.want)
|
t.Errorf("Got %v for %q, want: %v", got, tc.domain, tc.want)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Test with storage disabled.
|
// Test with default reject.
|
||||||
ap = &policy.Addressing{
|
ap = &policy.Addressing{
|
||||||
Config: config.SMTP{
|
Config: config.SMTP{
|
||||||
StoreMessages: false,
|
DefaultAccept: false,
|
||||||
|
AcceptDomains: []string{"a.allow.com", "allow.com"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
testCases = []struct {
|
testCases = []struct {
|
||||||
@@ -45,15 +46,68 @@ func TestShouldStoreDomain(t *testing.T) {
|
|||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{domain: "bar.com", want: false},
|
{domain: "bar.com", want: false},
|
||||||
|
{domain: "ALLOW.com", want: true},
|
||||||
|
{domain: "a.allow.com", want: true},
|
||||||
|
{domain: "b.allow.com", want: false},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.domain, func(t *testing.T) {
|
||||||
|
got := ap.ShouldAcceptDomain(tc.domain)
|
||||||
|
if got != tc.want {
|
||||||
|
t.Errorf("Got %v for %q, want: %v", got, tc.domain, tc.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldStoreDomain(t *testing.T) {
|
||||||
|
// Test with storage enabled.
|
||||||
|
ap := &policy.Addressing{
|
||||||
|
Config: config.SMTP{
|
||||||
|
DefaultStore: false,
|
||||||
|
StoreDomains: []string{"store.com", "a.store.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
domain string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
{domain: "foo.com", want: false},
|
{domain: "foo.com", want: false},
|
||||||
{domain: "FOO.com", want: false},
|
{domain: "STORE.com", want: true},
|
||||||
{domain: "bar.foo.com", want: false},
|
{domain: "a.store.com", want: true},
|
||||||
|
{domain: "b.store.com", want: false},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.domain, func(t *testing.T) {
|
t.Run(tc.domain, func(t *testing.T) {
|
||||||
got := ap.ShouldStoreDomain(tc.domain)
|
got := ap.ShouldStoreDomain(tc.domain)
|
||||||
if got != tc.want {
|
if got != tc.want {
|
||||||
t.Errorf("Got %v for %q, want: %v", got, tc.domain, tc.want)
|
t.Errorf("Got store %v for %q, want: %v", got, tc.domain, tc.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Test with storage disabled.
|
||||||
|
ap = &policy.Addressing{
|
||||||
|
Config: config.SMTP{
|
||||||
|
DefaultStore: true,
|
||||||
|
DiscardDomains: []string{"discard.com", "a.discard.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testCases = []struct {
|
||||||
|
domain string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{domain: "foo.com", want: true},
|
||||||
|
{domain: "DISCARD.com", want: false},
|
||||||
|
{domain: "a.discard.com", want: false},
|
||||||
|
{domain: "b.discard.com", want: true},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.domain, func(t *testing.T) {
|
||||||
|
got := ap.ShouldStoreDomain(tc.domain)
|
||||||
|
if got != tc.want {
|
||||||
|
t.Errorf("Got store %v for %q, want: %v", got, tc.domain, tc.want)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import "net/mail"
|
|||||||
// Recipient represents a potential email recipient, allows policies for it to be queried.
|
// Recipient represents a potential email recipient, allows policies for it to be queried.
|
||||||
type Recipient struct {
|
type Recipient struct {
|
||||||
mail.Address
|
mail.Address
|
||||||
apolicy *Addressing
|
addrPolicy *Addressing
|
||||||
// LocalPart is the part of the address before @, including +extension.
|
// LocalPart is the part of the address before @, including +extension.
|
||||||
LocalPart string
|
LocalPart string
|
||||||
// Domain is the part of the address after @.
|
// Domain is the part of the address after @.
|
||||||
@@ -16,10 +16,10 @@ type Recipient struct {
|
|||||||
|
|
||||||
// ShouldAccept returns true if Inbucket should accept mail for this recipient.
|
// ShouldAccept returns true if Inbucket should accept mail for this recipient.
|
||||||
func (r *Recipient) ShouldAccept() bool {
|
func (r *Recipient) ShouldAccept() bool {
|
||||||
return true
|
return r.addrPolicy.ShouldAcceptDomain(r.Domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldStore returns true if Inbucket should store mail for this recipient.
|
// ShouldStore returns true if Inbucket should store mail for this recipient.
|
||||||
func (r *Recipient) ShouldStore() bool {
|
func (r *Recipient) ShouldStore() bool {
|
||||||
return r.apolicy.ShouldStoreDomain(r.Domain)
|
return r.addrPolicy.ShouldStoreDomain(r.Domain)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -329,6 +329,11 @@ func (s *Session) mailHandler(cmd string, arg string) {
|
|||||||
s.logger.Warn().Msgf("Bad address as RCPT arg: %q, %s", addr, err)
|
s.logger.Warn().Msgf("Bad address as RCPT arg: %q, %s", addr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !recip.ShouldAccept() {
|
||||||
|
s.logger.Warn().Str("addr", addr).Msg("Rejecting recipient")
|
||||||
|
s.send("550 Relay not permitted")
|
||||||
|
return
|
||||||
|
}
|
||||||
if len(s.recipients) >= s.config.MaxRecipients {
|
if len(s.recipients) >= s.config.MaxRecipients {
|
||||||
s.logger.Warn().Msgf("Maximum limit of %v recipients reached",
|
s.logger.Warn().Msgf("Maximum limit of %v recipients reached",
|
||||||
s.config.MaxRecipients)
|
s.config.MaxRecipients)
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ func TestMailState(t *testing.T) {
|
|||||||
{"RCPT TO:<u1@gmail.com>", 250},
|
{"RCPT TO:<u1@gmail.com>", 250},
|
||||||
{"RCPT TO: <u2@gmail.com>", 250},
|
{"RCPT TO: <u2@gmail.com>", 250},
|
||||||
{"RCPT TO:u3@gmail.com", 250},
|
{"RCPT TO:u3@gmail.com", 250},
|
||||||
|
{"RCPT TO:u3@deny.com", 550},
|
||||||
{"RCPT TO: u4@gmail.com", 250},
|
{"RCPT TO: u4@gmail.com", 250},
|
||||||
{"RSET", 250},
|
{"RSET", 250},
|
||||||
{"MAIL FROM:<john@gmail.com>", 250},
|
{"MAIL FROM:<john@gmail.com>", 250},
|
||||||
@@ -364,11 +365,11 @@ func setupSMTPServer(ds storage.Store) (s *Server, buf *bytes.Buffer, teardown f
|
|||||||
cfg := config.SMTP{
|
cfg := config.SMTP{
|
||||||
Addr: "127.0.0.1:2500",
|
Addr: "127.0.0.1:2500",
|
||||||
Domain: "inbucket.local",
|
Domain: "inbucket.local",
|
||||||
DomainNoStore: "bitbucket.local",
|
|
||||||
MaxRecipients: 5,
|
MaxRecipients: 5,
|
||||||
Timeout: 5,
|
|
||||||
MaxMessageBytes: 5000,
|
MaxMessageBytes: 5000,
|
||||||
StoreMessages: true,
|
DefaultAccept: true,
|
||||||
|
RejectDomains: []string{"deny.com"},
|
||||||
|
Timeout: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture log output
|
// Capture log output
|
||||||
|
|||||||
@@ -97,11 +97,6 @@ func (s *Server) Start(ctx context.Context) {
|
|||||||
s.emergencyShutdown()
|
s.emergencyShutdown()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !s.config.StoreMessages {
|
|
||||||
slog.Info().Msg("Load test mode active, messages will not be stored")
|
|
||||||
} else if s.config.DomainNoStore != "" {
|
|
||||||
slog.Info().Msgf("Messages sent to domain '%v' will be discarded", s.config.DomainNoStore)
|
|
||||||
}
|
|
||||||
// Listener go routine.
|
// Listener go routine.
|
||||||
go s.serve(ctx)
|
go s.serve(ctx)
|
||||||
// Wait for shutdown.
|
// Wait for shutdown.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
var TemplateFuncs = template.FuncMap{
|
var TemplateFuncs = template.FuncMap{
|
||||||
"friendlyTime": FriendlyTime,
|
"friendlyTime": FriendlyTime,
|
||||||
"reverse": Reverse,
|
"reverse": Reverse,
|
||||||
|
"stringsJoin": strings.Join,
|
||||||
"textToHtml": TextToHTML,
|
"textToHtml": TextToHTML,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HashMailboxName accepts a mailbox name and hashes it. filestore uses this as
|
// HashMailboxName accepts a mailbox name and hashes it. filestore uses this as
|
||||||
@@ -28,3 +29,20 @@ func StringAddressList(addrs []*mail.Address) []string {
|
|||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SliceContains returns true if s is present in slice.
|
||||||
|
func SliceContains(slice []string, s string) bool {
|
||||||
|
for _, v := range slice {
|
||||||
|
if s == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceToLower lowercases the contents of slice of strings.
|
||||||
|
func SliceToLower(slice []string) {
|
||||||
|
for i, s := range slice {
|
||||||
|
slice[i] = strings.ToLower(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,10 +52,30 @@ $(document).ready(
|
|||||||
<div class="col-sm-8 col-xs-5"><span>{{.webListener}}</span></div>
|
<div class="col-sm-8 col-xs-5"><span>{{.webListener}}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 col-xs-7"><b>No-Store Domain:</b></div>
|
<div class="col-sm-3 col-xs-7"><b>Accept Policy:</b></div>
|
||||||
<div class="col-sm-8 col-xs-5">
|
<div class="col-sm-8 col-xs-5">
|
||||||
{{with .smtpConfig}}
|
{{with .smtpConfig}}
|
||||||
<span>{{or .DomainNoStore .DomainNoStore "Not Configured"}}</span>
|
<span>
|
||||||
|
{{if .DefaultAccept}}
|
||||||
|
All domains{{with .RejectDomains}}, except: {{stringsJoin . ", "}}{{end}}
|
||||||
|
{{else}}
|
||||||
|
No domains{{with .AcceptDomains}}, except: {{stringsJoin . ", "}}{{end}}
|
||||||
|
{{end}}
|
||||||
|
</span>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 col-xs-7"><b>Store Policy:</b></div>
|
||||||
|
<div class="col-sm-8 col-xs-5">
|
||||||
|
{{with .smtpConfig}}
|
||||||
|
<span>
|
||||||
|
{{if .DefaultStore}}
|
||||||
|
All domains{{with .DiscardDomains}}, except: {{stringsJoin . ", "}}{{end}}
|
||||||
|
{{else}}
|
||||||
|
No domains{{with .StoreDomains}}, except: {{stringsJoin . ", "}}{{end}}
|
||||||
|
{{end}}
|
||||||
|
</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user