mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 09:37:02 +00:00
- All packages go into either cmd or pkg directories - Most packages renamed - Server packages moved into pkg/server - sanitize moved into webui, as that's the only place it's used - filestore moved into pkg/storage/file - Makefile updated, and PKG variable use fixed
271 lines
7.4 KiB
Go
271 lines
7.4 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/robfig/config"
|
|
)
|
|
|
|
// SMTPConfig contains the SMTP server configuration - not using pointers so that we can pass around
|
|
// copies of the object safely.
|
|
type SMTPConfig struct {
|
|
IP4address net.IP
|
|
IP4port int
|
|
Domain string
|
|
DomainNoStore string
|
|
MaxRecipients int
|
|
MaxIdleSeconds int
|
|
MaxMessageBytes int
|
|
StoreMessages bool
|
|
}
|
|
|
|
// POP3Config contains the POP3 server configuration
|
|
type POP3Config struct {
|
|
IP4address net.IP
|
|
IP4port int
|
|
Domain string
|
|
MaxIdleSeconds int
|
|
}
|
|
|
|
// WebConfig contains the HTTP server configuration
|
|
type WebConfig struct {
|
|
IP4address net.IP
|
|
IP4port int
|
|
TemplateDir string
|
|
TemplateCache bool
|
|
PublicDir string
|
|
GreetingFile string
|
|
MailboxPrompt string
|
|
CookieAuthKey string
|
|
MonitorVisible bool
|
|
MonitorHistory int
|
|
}
|
|
|
|
// DataStoreConfig contains the mail store configuration
|
|
type DataStoreConfig struct {
|
|
Path string
|
|
RetentionMinutes int
|
|
RetentionSleep int
|
|
MailboxMsgCap int
|
|
}
|
|
|
|
const (
|
|
missingErrorFmt = "[%v] missing required option %q"
|
|
parseErrorFmt = "[%v] option %q error: %v"
|
|
)
|
|
|
|
var (
|
|
// Version of this build, set by main
|
|
Version = ""
|
|
|
|
// BuildDate for this build, set by main
|
|
BuildDate = ""
|
|
|
|
// Config is our global robfig/config object
|
|
Config *config.Config
|
|
logLevel string
|
|
|
|
// Parsed specific configs
|
|
smtpConfig = &SMTPConfig{}
|
|
pop3Config = &POP3Config{}
|
|
webConfig = &WebConfig{}
|
|
dataStoreConfig = &DataStoreConfig{}
|
|
)
|
|
|
|
// GetSMTPConfig returns a copy of the SmtpConfig object
|
|
func GetSMTPConfig() SMTPConfig {
|
|
return *smtpConfig
|
|
}
|
|
|
|
// GetPOP3Config returns a copy of the Pop3Config object
|
|
func GetPOP3Config() POP3Config {
|
|
return *pop3Config
|
|
}
|
|
|
|
// GetWebConfig returns a copy of the WebConfig object
|
|
func GetWebConfig() WebConfig {
|
|
return *webConfig
|
|
}
|
|
|
|
// GetDataStoreConfig returns a copy of the DataStoreConfig object
|
|
func GetDataStoreConfig() DataStoreConfig {
|
|
return *dataStoreConfig
|
|
}
|
|
|
|
// GetLogLevel returns the configured log level
|
|
func GetLogLevel() string {
|
|
return logLevel
|
|
}
|
|
|
|
// LoadConfig loads the specified configuration file into inbucket.Config and performs validations
|
|
// on it.
|
|
func LoadConfig(filename string) error {
|
|
var err error
|
|
Config, err = config.ReadDefault(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Validation error messages
|
|
messages := make([]string, 0)
|
|
// Validate sections
|
|
for _, s := range []string{"logging", "smtp", "pop3", "web", "datastore"} {
|
|
if !Config.HasSection(s) {
|
|
messages = append(messages,
|
|
fmt.Sprintf("Config section [%v] is required", s))
|
|
}
|
|
}
|
|
// Return immediately if config is missing entire sections
|
|
if len(messages) > 0 {
|
|
fmt.Fprintln(os.Stderr, "Error(s) validating configuration:")
|
|
for _, m := range messages {
|
|
fmt.Fprintln(os.Stderr, " -", m)
|
|
}
|
|
return fmt.Errorf("Failed to validate configuration")
|
|
}
|
|
// Load string config options
|
|
stringOptions := []struct {
|
|
section string
|
|
name string
|
|
target *string
|
|
required bool
|
|
}{
|
|
{"logging", "level", &logLevel, true},
|
|
{"smtp", "domain", &smtpConfig.Domain, true},
|
|
{"smtp", "domain.nostore", &smtpConfig.DomainNoStore, false},
|
|
{"pop3", "domain", &pop3Config.Domain, true},
|
|
{"web", "template.dir", &webConfig.TemplateDir, true},
|
|
{"web", "public.dir", &webConfig.PublicDir, true},
|
|
{"web", "greeting.file", &webConfig.GreetingFile, true},
|
|
{"web", "mailbox.prompt", &webConfig.MailboxPrompt, false},
|
|
{"web", "cookie.auth.key", &webConfig.CookieAuthKey, false},
|
|
{"datastore", "path", &dataStoreConfig.Path, true},
|
|
}
|
|
for _, opt := range stringOptions {
|
|
str, err := Config.String(opt.section, opt.name)
|
|
if Config.HasOption(opt.section, opt.name) && err != nil {
|
|
messages = append(messages, fmt.Sprintf(parseErrorFmt, opt.section, opt.name, err))
|
|
continue
|
|
}
|
|
if str == "" && opt.required {
|
|
messages = append(messages, fmt.Sprintf(missingErrorFmt, opt.section, opt.name))
|
|
}
|
|
*opt.target = str
|
|
}
|
|
// Load boolean config options
|
|
boolOptions := []struct {
|
|
section string
|
|
name string
|
|
target *bool
|
|
required bool
|
|
}{
|
|
{"smtp", "store.messages", &smtpConfig.StoreMessages, true},
|
|
{"web", "template.cache", &webConfig.TemplateCache, true},
|
|
{"web", "monitor.visible", &webConfig.MonitorVisible, true},
|
|
}
|
|
for _, opt := range boolOptions {
|
|
if Config.HasOption(opt.section, opt.name) {
|
|
flag, err := Config.Bool(opt.section, opt.name)
|
|
if err != nil {
|
|
messages = append(messages, fmt.Sprintf(parseErrorFmt, opt.section, opt.name, err))
|
|
}
|
|
*opt.target = flag
|
|
} else {
|
|
if opt.required {
|
|
messages = append(messages, fmt.Sprintf(missingErrorFmt, opt.section, opt.name))
|
|
}
|
|
}
|
|
}
|
|
// Load integer config options
|
|
intOptions := []struct {
|
|
section string
|
|
name string
|
|
target *int
|
|
required bool
|
|
}{
|
|
{"smtp", "ip4.port", &smtpConfig.IP4port, true},
|
|
{"smtp", "max.recipients", &smtpConfig.MaxRecipients, true},
|
|
{"smtp", "max.idle.seconds", &smtpConfig.MaxIdleSeconds, true},
|
|
{"smtp", "max.message.bytes", &smtpConfig.MaxMessageBytes, true},
|
|
{"pop3", "ip4.port", &pop3Config.IP4port, true},
|
|
{"pop3", "max.idle.seconds", &pop3Config.MaxIdleSeconds, true},
|
|
{"web", "ip4.port", &webConfig.IP4port, true},
|
|
{"web", "monitor.history", &webConfig.MonitorHistory, true},
|
|
{"datastore", "retention.minutes", &dataStoreConfig.RetentionMinutes, true},
|
|
{"datastore", "retention.sleep.millis", &dataStoreConfig.RetentionSleep, true},
|
|
{"datastore", "mailbox.message.cap", &dataStoreConfig.MailboxMsgCap, true},
|
|
}
|
|
for _, opt := range intOptions {
|
|
if Config.HasOption(opt.section, opt.name) {
|
|
num, err := Config.Int(opt.section, opt.name)
|
|
if err != nil {
|
|
messages = append(messages, fmt.Sprintf(parseErrorFmt, opt.section, opt.name, err))
|
|
}
|
|
*opt.target = num
|
|
} else {
|
|
if opt.required {
|
|
messages = append(messages, fmt.Sprintf(missingErrorFmt, opt.section, opt.name))
|
|
}
|
|
}
|
|
}
|
|
// Load IP address config options
|
|
ipOptions := []struct {
|
|
section string
|
|
name string
|
|
target *net.IP
|
|
required bool
|
|
}{
|
|
{"smtp", "ip4.address", &smtpConfig.IP4address, true},
|
|
{"pop3", "ip4.address", &pop3Config.IP4address, true},
|
|
{"web", "ip4.address", &webConfig.IP4address, true},
|
|
}
|
|
for _, opt := range ipOptions {
|
|
if Config.HasOption(opt.section, opt.name) {
|
|
str, err := Config.String(opt.section, opt.name)
|
|
if err != nil {
|
|
messages = append(messages, fmt.Sprintf(parseErrorFmt, opt.section, opt.name, err))
|
|
continue
|
|
}
|
|
addr := net.ParseIP(str)
|
|
if addr == nil {
|
|
messages = append(messages,
|
|
fmt.Sprintf("Failed to parse IP [%v]%v: %q", opt.section, opt.name, str))
|
|
continue
|
|
}
|
|
addr = addr.To4()
|
|
if addr == nil {
|
|
messages = append(messages,
|
|
fmt.Sprintf("Failed to parse IP [%v]%v: %q not IPv4!",
|
|
opt.section, opt.name, str))
|
|
}
|
|
*opt.target = addr
|
|
} else {
|
|
if opt.required {
|
|
messages = append(messages, fmt.Sprintf(missingErrorFmt, opt.section, opt.name))
|
|
}
|
|
}
|
|
}
|
|
// Validate log level
|
|
switch strings.ToUpper(logLevel) {
|
|
case "":
|
|
// Missing was already reported
|
|
case "TRACE", "INFO", "WARN", "ERROR":
|
|
default:
|
|
messages = append(messages,
|
|
fmt.Sprintf("Invalid value provided for [logging]level: %q", logLevel))
|
|
}
|
|
// Print messages and return error if any validations failed
|
|
if len(messages) > 0 {
|
|
fmt.Fprintln(os.Stderr, "Error(s) validating configuration:")
|
|
sort.Strings(messages)
|
|
for _, m := range messages {
|
|
fmt.Fprintln(os.Stderr, " -", m)
|
|
}
|
|
return fmt.Errorf("Failed to validate configuration")
|
|
}
|
|
return nil
|
|
}
|