1
0
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:
James Hillyerd
2024-02-16 14:27:04 -08:00
committed by GitHub
parent def3e88651
commit 0361e971e0
9 changed files with 78 additions and 75 deletions

View File

@@ -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 {

View File

@@ -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.

View File

@@ -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)
} }

View File

@@ -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)
}
} }
}) })
} }

View File

@@ -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, ""
} }

View File

@@ -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() {

View File

@@ -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)

View File

@@ -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)

View File

@@ -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]