mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 09:37:02 +00:00
chore: many small lint/perf fixes (#491)
Signed-off-by: James Hillyerd <james@hillyerd.com>
This commit is contained in:
@@ -5,8 +5,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/google/subcommands"
|
"github.com/google/subcommands"
|
||||||
)
|
)
|
||||||
@@ -68,7 +70,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func baseURL() string {
|
func baseURL() string {
|
||||||
return fmt.Sprintf("http://%s:%v", *host, *port)
|
return fmt.Sprintf("http://%s",
|
||||||
|
net.JoinHostPort(*host, strconv.FormatUint(uint64(*port), 10)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func fatal(msg string, err error) subcommands.ExitStatus {
|
func fatal(msg string, err error) subcommands.ExitStatus {
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ signalLoop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// openLog configures zerolog output, returns func to close logfile.
|
// openLog configures zerolog output, returns func to close logfile.
|
||||||
func openLog(level string, logfile string, json bool) (close func(), err error) {
|
func openLog(level string, logfile string, json bool) (closeLog func(), err error) {
|
||||||
switch level {
|
switch level {
|
||||||
case "debug":
|
case "debug":
|
||||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||||
@@ -175,7 +175,8 @@ func openLog(level string, logfile string, json bool) (close func(), err error)
|
|||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("log level %q not one of: debug, info, warn, error", level)
|
return nil, fmt.Errorf("log level %q not one of: debug, info, warn, error", level)
|
||||||
}
|
}
|
||||||
close = func() {}
|
|
||||||
|
closeLog = func() {}
|
||||||
var w io.Writer
|
var w io.Writer
|
||||||
color := runtime.GOOS != "windows"
|
color := runtime.GOOS != "windows"
|
||||||
switch logfile {
|
switch logfile {
|
||||||
@@ -191,21 +192,24 @@ func openLog(level string, logfile string, json bool) (close func(), err error)
|
|||||||
bw := bufio.NewWriter(logf)
|
bw := bufio.NewWriter(logf)
|
||||||
w = bw
|
w = bw
|
||||||
color = false
|
color = false
|
||||||
close = func() {
|
closeLog = func() {
|
||||||
_ = bw.Flush()
|
_ = bw.Flush()
|
||||||
_ = logf.Close()
|
_ = logf.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w = zerolog.SyncWriter(w)
|
w = zerolog.SyncWriter(w)
|
||||||
if json {
|
if json {
|
||||||
log.Logger = log.Output(w)
|
log.Logger = log.Output(w)
|
||||||
return close, nil
|
return closeLog, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{
|
log.Logger = log.Output(zerolog.ConsoleWriter{
|
||||||
Out: w,
|
Out: w,
|
||||||
NoColor: !color,
|
NoColor: !color,
|
||||||
})
|
})
|
||||||
return close, nil
|
|
||||||
|
return closeLog, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// removePIDFile removes the PID file if created.
|
// removePIDFile removes the PID file if created.
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ LOOP:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
inCharQuote = false
|
inCharQuote = false
|
||||||
case bytes.IndexByte([]byte("!#$%&'*+-/=?^_`{|}~"), c) >= 0:
|
case strings.IndexByte("!#$%&'*+-/=?^_`{|}~", c) >= 0:
|
||||||
// These specials can be used unquoted.
|
// These specials can be used unquoted.
|
||||||
err = buf.WriteByte(c)
|
err = buf.WriteByte(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -378,7 +378,7 @@ func parseMailboxName(localPart string) (result string, err error) {
|
|||||||
switch {
|
switch {
|
||||||
case 'a' <= c && c <= 'z':
|
case 'a' <= c && c <= 'z':
|
||||||
case '0' <= c && c <= '9':
|
case '0' <= c && c <= '9':
|
||||||
case bytes.IndexByte([]byte("!#$%&'*+-=/?^_`.{|}~"), c) >= 0:
|
case strings.IndexByte("!#$%&'*+-=/?^_`.{|}~", c) >= 0:
|
||||||
default:
|
default:
|
||||||
invalid = append(invalid, c)
|
invalid = append(invalid, c)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,24 +283,18 @@ func TestExtractMailboxValid(t *testing.T) {
|
|||||||
for _, tc := range testTable {
|
for _, tc := range testTable {
|
||||||
if result, err := localPolicy.ExtractMailbox(tc.input); err != nil {
|
if result, err := localPolicy.ExtractMailbox(tc.input); err != nil {
|
||||||
t.Errorf("Error while parsing with local naming %q: %v", tc.input, err)
|
t.Errorf("Error while parsing with local naming %q: %v", tc.input, err)
|
||||||
} else {
|
} else if result != tc.local {
|
||||||
if result != tc.local {
|
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.local, result)
|
||||||
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.local, result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if result, err := fullPolicy.ExtractMailbox(tc.input); err != nil {
|
if result, err := fullPolicy.ExtractMailbox(tc.input); err != nil {
|
||||||
t.Errorf("Error while parsing with full naming %q: %v", tc.input, err)
|
t.Errorf("Error while parsing with full naming %q: %v", tc.input, err)
|
||||||
} else {
|
} else if result != tc.full {
|
||||||
if result != tc.full {
|
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.full, result)
|
||||||
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.full, result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if result, err := domainPolicy.ExtractMailbox(tc.input); tc.domain != "" && err != nil {
|
if result, err := domainPolicy.ExtractMailbox(tc.input); tc.domain != "" && err != nil {
|
||||||
t.Errorf("Error while parsing with domain naming %q: %v", tc.input, err)
|
t.Errorf("Error while parsing with domain naming %q: %v", tc.input, err)
|
||||||
} else {
|
} else if result != tc.domain {
|
||||||
if result != tc.domain {
|
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.domain, result)
|
||||||
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.domain, result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,10 +321,8 @@ func TestExtractDomainMailboxValid(t *testing.T) {
|
|||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
if result, err := domainPolicy.ExtractMailbox(tc.input); tc.domain != "" && err != nil {
|
if result, err := domainPolicy.ExtractMailbox(tc.input); tc.domain != "" && err != nil {
|
||||||
t.Errorf("Error while parsing with domain naming %q: %v", tc.input, err)
|
t.Errorf("Error while parsing with domain naming %q: %v", tc.input, err)
|
||||||
} else {
|
} else if result != tc.domain {
|
||||||
if result != tc.domain {
|
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.domain, result)
|
||||||
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.domain, result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,9 +122,10 @@ func getDecodedPath(o interface{}, path ...string) (interface{}, string) {
|
|||||||
if o == nil {
|
if o == nil {
|
||||||
return nil, " is nil"
|
return nil, " is nil"
|
||||||
}
|
}
|
||||||
key := path[0]
|
|
||||||
present := false
|
var present bool
|
||||||
var val interface{}
|
var val interface{}
|
||||||
|
key := path[0]
|
||||||
if key[0] == '[' {
|
if key[0] == '[' {
|
||||||
// Expecting slice.
|
// Expecting slice.
|
||||||
index, err := strconv.Atoi(strings.Trim(key, "[]"))
|
index, err := strconv.Atoi(strings.Trim(key, "[]"))
|
||||||
@@ -147,12 +148,15 @@ func getDecodedPath(o interface{}, path ...string) (interface{}, string) {
|
|||||||
}
|
}
|
||||||
val, present = omap[key]
|
val, present = omap[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
if !present {
|
if !present {
|
||||||
return nil, "/" + key + " is missing"
|
return nil, "/" + key + " is missing"
|
||||||
}
|
}
|
||||||
|
|
||||||
result, msg := getDecodedPath(val, path[1:]...)
|
result, msg := getDecodedPath(val, path[1:]...)
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
return nil, "/" + key + msg
|
return nil, "/" + key + msg
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, ""
|
return result, ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,48 +134,46 @@ func (s *Server) startSession(id int, conn net.Conn) {
|
|||||||
line, err := ssn.readLine()
|
line, err := ssn.readLine()
|
||||||
ssn.logger.Debug().Msgf("read %s", line)
|
ssn.logger.Debug().Msgf("read %s", line)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if cmd, arg, ok := ssn.parseCmd(line); ok {
|
cmd, arg := ssn.parseCmd(line)
|
||||||
// Check against valid SMTP commands
|
// Commands we handle in any state
|
||||||
if cmd == "" {
|
if cmd == "CAPA" {
|
||||||
ssn.send("-ERR Speak up")
|
// List our capabilities per RFC2449
|
||||||
continue
|
ssn.send("+OK Capability list follows")
|
||||||
|
ssn.send("TOP")
|
||||||
|
ssn.send("USER")
|
||||||
|
ssn.send("UIDL")
|
||||||
|
ssn.send("IMPLEMENTATION Inbucket")
|
||||||
|
if s.tlsConfig != nil && s.tlsState == nil && !s.config.ForceTLS {
|
||||||
|
ssn.send("STLS")
|
||||||
}
|
}
|
||||||
if !commands[cmd] {
|
ssn.send(".")
|
||||||
ssn.send(fmt.Sprintf("-ERR Syntax error, %v command unrecognized", cmd))
|
continue
|
||||||
ssn.logger.Warn().Msgf("Unrecognized command: %v", cmd)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commands we handle in any state
|
|
||||||
switch cmd {
|
|
||||||
case "CAPA":
|
|
||||||
// List our capabilities per RFC2449
|
|
||||||
ssn.send("+OK Capability list follows")
|
|
||||||
ssn.send("TOP")
|
|
||||||
ssn.send("USER")
|
|
||||||
ssn.send("UIDL")
|
|
||||||
ssn.send("IMPLEMENTATION Inbucket")
|
|
||||||
if s.tlsConfig != nil && s.tlsState == nil && !s.config.ForceTLS {
|
|
||||||
ssn.send("STLS")
|
|
||||||
}
|
|
||||||
ssn.send(".")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send command to handler for current state
|
|
||||||
switch ssn.state {
|
|
||||||
case AUTHORIZATION:
|
|
||||||
ssn.authorizationHandler(cmd, arg)
|
|
||||||
continue
|
|
||||||
case TRANSACTION:
|
|
||||||
ssn.transactionHandler(cmd, arg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ssn.logger.Error().Msgf("Session entered unexpected state %v", ssn.state)
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
ssn.send("-ERR Syntax error, command garbled")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check against valid SMTP commands
|
||||||
|
if cmd == "" {
|
||||||
|
ssn.send("-ERR Speak up")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !commands[cmd] {
|
||||||
|
ssn.send(fmt.Sprintf("-ERR Syntax error, %v command unrecognized", cmd))
|
||||||
|
ssn.logger.Warn().Msgf("Unrecognized command: %v", cmd)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send command to handler for current state
|
||||||
|
switch ssn.state {
|
||||||
|
case AUTHORIZATION:
|
||||||
|
ssn.authorizationHandler(cmd, arg)
|
||||||
|
continue
|
||||||
|
case TRANSACTION:
|
||||||
|
ssn.transactionHandler(cmd, arg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ssn.logger.Error().Msgf("Session entered unexpected state %v", ssn.state)
|
||||||
|
break
|
||||||
} else {
|
} else {
|
||||||
// readLine() returned an error
|
// readLine() returned an error
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -631,14 +629,14 @@ func (s *Session) readLine() (line string, err error) {
|
|||||||
return line, nil
|
return line, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) parseCmd(line string) (cmd string, args []string, ok bool) {
|
func (s *Session) parseCmd(line string) (cmd string, args []string) {
|
||||||
line = strings.TrimRight(line, "\r\n")
|
line = strings.TrimRight(line, "\r\n")
|
||||||
if line == "" {
|
if line == "" {
|
||||||
return "", nil, true
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
words := strings.Split(line, " ")
|
words := strings.Split(line, " ")
|
||||||
return strings.ToUpper(words[0]), words[1:], true
|
return strings.ToUpper(words[0]), words[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) reset() {
|
func (s *Session) reset() {
|
||||||
|
|||||||
@@ -173,13 +173,13 @@ func (s *Server) startSession(id int, conn net.Conn, logger zerolog.Logger) {
|
|||||||
}
|
}
|
||||||
line, err := ssn.readLine()
|
line, err := ssn.readLine()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//Handle LOGIN/PASSWORD states here, because they don't expect a command
|
// Handle LOGIN/PASSWORD states here, because they don't expect a command.
|
||||||
switch ssn.state {
|
switch ssn.state {
|
||||||
case LOGIN:
|
case LOGIN:
|
||||||
ssn.loginHandler(line)
|
ssn.loginHandler()
|
||||||
continue
|
continue
|
||||||
case PASSWORD:
|
case PASSWORD:
|
||||||
ssn.passwordHandler(line)
|
ssn.passwordHandler()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,13 +312,13 @@ func parseHelloArgument(arg string) (string, error) {
|
|||||||
return domain, nil
|
return domain, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) loginHandler(line string) {
|
func (s *Session) loginHandler() {
|
||||||
// Content and length of username is ignored.
|
// Content and length of username is ignored.
|
||||||
s.send(fmt.Sprintf("334 %v", passwordChallenge))
|
s.send(fmt.Sprintf("334 %v", passwordChallenge))
|
||||||
s.enterState(PASSWORD)
|
s.enterState(PASSWORD)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) passwordHandler(line string) {
|
func (s *Session) passwordHandler() {
|
||||||
// Content and length of password is ignored.
|
// Content and length of password is ignored.
|
||||||
s.send("235 Authentication successful")
|
s.send("235 Authentication successful")
|
||||||
s.enterState(READY)
|
s.enterState(READY)
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ func (mb *mbox) readIndex() error {
|
|||||||
log.Debug().Str("module", "storage").Str("path", mb.indexPath).
|
log.Debug().Str("module", "storage").Str("path", mb.indexPath).
|
||||||
Msg("Index does not yet exist")
|
Msg("Index does not yet exist")
|
||||||
mb.indexLoaded = true
|
mb.indexLoaded = true
|
||||||
|
|
||||||
|
//lint:ignore nilerr missing mailboxes are considered empty.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
file, err := os.Open(mb.indexPath)
|
file, err := os.Open(mb.indexPath)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func sanitizeStyleTags(input string) (string, error) {
|
|||||||
|
|
||||||
func styleTagFilter(w io.Writer, r io.Reader) error {
|
func styleTagFilter(w io.Writer, r io.Reader) error {
|
||||||
bw := bufio.NewWriter(w)
|
bw := bufio.NewWriter(w)
|
||||||
b := make([]byte, 256)
|
b := make([]byte, 0, 256)
|
||||||
z := html.NewTokenizer(r)
|
z := html.NewTokenizer(r)
|
||||||
for {
|
for {
|
||||||
b = b[:0]
|
b = b[:0]
|
||||||
|
|||||||
Reference in New Issue
Block a user