From f99d9ecf693b1dfe24bcfc62885eab083e409f12 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Sun, 22 Jun 2025 10:32:03 +1200 Subject: [PATCH] Chore: Refactor error handling and resource management across multiple files (golangci-lint) - Updated error handling to use the error return value for resource closures in tests and functions, ensuring proper error reporting. - Replaced direct calls to `Close()` with deferred functions that handle errors gracefully. - Improved readability by using `strings.ReplaceAll` instead of `strings.Replace` for string manipulation. - Enhanced network connection handling by adding default cases for unsupported network types. - Updated HTTP response handling to use the appropriate status codes and error messages. - Removed unused variables and commented-out code to clean up the codebase. --- cmd/ingest.go | 2 +- config/utils.go | 2 +- internal/dump/dump.go | 6 +- internal/htmlcheck/css.go | 4 +- internal/htmlcheck/main.go | 7 +- internal/pop3/functions.go | 4 +- internal/pop3/pop3_test.go | 53 ++--- internal/pop3/server.go | 2 +- internal/pop3client/client.go | 2 +- internal/prometheus/metrics.go | 6 +- internal/smtpd/forward.go | 6 +- internal/smtpd/relay.go | 8 +- internal/smtpd/smtpd.go | 14 +- internal/smtpd/smtpd_test.go | 229 +++++++++++---------- internal/spamassassin/postmark/postmark.go | 2 +- internal/spamassassin/spamc/spamc.go | 13 +- internal/storage/database.go | 2 - internal/storage/messages.go | 8 +- internal/storage/reindex.go | 2 +- internal/storage/search.go | 2 +- internal/tools/fs.go | 2 +- internal/tools/listunsubscribeparser.go | 2 +- internal/updater/targz.go | 2 +- internal/updater/unzip.go | 2 +- internal/updater/updater.go | 11 +- sendmail/cmd/cmd.go | 8 +- server/apiv1/api.go | 4 +- server/apiv1/message.go | 8 +- server/apiv1/other.go | 2 +- server/apiv1/testing.go | 10 +- server/handlers/messages.go | 2 +- server/handlers/proxy.go | 7 +- server/server.go | 7 +- server/server_test.go | 37 ++-- server/webhook/webhook.go | 4 +- 35 files changed, 250 insertions(+), 232 deletions(-) diff --git a/cmd/ingest.go b/cmd/ingest.go index fa61028..f68ed18 100644 --- a/cmd/ingest.go +++ b/cmd/ingest.go @@ -55,7 +55,7 @@ The --recent flag will only consider files with a modification date within the l logger.Log().Errorf("%s: %s", path, err.Error()) return nil } - defer f.Close() // #nosec + defer func() { _ = f.Close() }() body, err := io.ReadAll(f) if err != nil { diff --git a/config/utils.go b/config/utils.go index b4d846a..a032cd1 100644 --- a/config/utils.go +++ b/config/utils.go @@ -13,7 +13,7 @@ import ( // IsFile returns whether a file exists and is readable func isFile(path string) bool { f, err := os.Open(filepath.Clean(path)) - defer f.Close() + defer func() { _ = f.Close() }() return err == nil } diff --git a/internal/dump/dump.go b/internal/dump/dump.go index bbf9539..3ff90f6 100644 --- a/internal/dump/dump.go +++ b/internal/dump/dump.go @@ -39,14 +39,14 @@ func Sync(d string) error { if URL != "" { if !linkRe.MatchString(URL) { - return errors.New("Invalid URL") + return errors.New("invalid URL") } base = strings.TrimRight(URL, "/") + "/" } if base == "" && config.Database == "" { - return errors.New("No database or API URL specified") + return errors.New("no database or API URL specified") } if !tools.IsDir(outDir) { @@ -109,7 +109,7 @@ func loadIDs() error { } if len(summary) == 0 { - return errors.New("No messages found") + return errors.New("no messages found") } return nil diff --git a/internal/htmlcheck/css.go b/internal/htmlcheck/css.go index 6022084..840b434 100644 --- a/internal/htmlcheck/css.go +++ b/internal/htmlcheck/css.go @@ -193,10 +193,10 @@ func downloadToBytes(url string) ([]byte, error) { if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { - err := fmt.Errorf("Error downloading %s", url) + err := fmt.Errorf("error downloading %s", url) return nil, err } diff --git a/internal/htmlcheck/main.go b/internal/htmlcheck/main.go index e953102..1b2e028 100644 --- a/internal/htmlcheck/main.go +++ b/internal/htmlcheck/main.go @@ -152,13 +152,14 @@ func (c CanIEmail) getTest(k string) (Warning, error) { s.Platform = platform s.Version = version - if support == "y" { + switch support { + case "y": y++ s.Support = "yes" - } else if support == "n" { + case "n": n++ s.Support = "no" - } else { + default: p++ s.Support = "partial" diff --git a/internal/pop3/functions.go b/internal/pop3/functions.go index ccaeecb..943a092 100644 --- a/internal/pop3/functions.go +++ b/internal/pop3/functions.go @@ -18,7 +18,7 @@ func authUser(username, password string) bool { // Send a response with debug logging func sendResponse(c net.Conn, m string) { - fmt.Fprintf(c, "%s\r\n", m) + _, _ = fmt.Fprintf(c, "%s\r\n", m) logger.Log().Debugf("[pop3] response: %s", m) if strings.HasPrefix(m, "-ERR ") { @@ -29,7 +29,7 @@ func sendResponse(c net.Conn, m string) { // Send a response without debug logging (for data) func sendData(c net.Conn, m string) { - fmt.Fprintf(c, "%s\r\n", m) + _, _ = fmt.Fprintf(c, "%s\r\n", m) } // Get the latest 100 messages diff --git a/internal/pop3/pop3_test.go b/internal/pop3/pop3_test.go index df34186..1f76ce4 100644 --- a/internal/pop3/pop3_test.go +++ b/internal/pop3/pop3_test.go @@ -29,22 +29,21 @@ func TestPOP3(t *testing.T) { // connect with bad password t.Log("Testing invalid login") - c, err := connectBadAuth() - if err == nil { + if _, err := connectBadAuth(); err == nil { t.Error("invalid login gained access") return } t.Log("Testing valid login") - c, err = connectAuth() + c, err := connectAuth() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } count, size, err := c.Stat() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -53,7 +52,7 @@ func TestPOP3(t *testing.T) { // quit else we get old data if err := c.Quit(); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -63,13 +62,13 @@ func TestPOP3(t *testing.T) { c, err = connectAuth() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } count, _, err = c.Stat() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -80,7 +79,7 @@ func TestPOP3(t *testing.T) { for i := 1; i <= 20; i++ { _, err := c.Retr(i) if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } } @@ -89,14 +88,14 @@ func TestPOP3(t *testing.T) { for i := 1; i <= 25; i++ { if err := c.Dele(i); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } } // messages get deleted after a QUIT if err := c.Quit(); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -105,7 +104,7 @@ func TestPOP3(t *testing.T) { c, err = connectAuth() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -113,7 +112,7 @@ func TestPOP3(t *testing.T) { count, _, err = c.Stat() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -121,13 +120,13 @@ func TestPOP3(t *testing.T) { // messages get deleted after a QUIT if err := c.Quit(); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } c, err = connectAuth() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -135,7 +134,7 @@ func TestPOP3(t *testing.T) { for i := 1; i <= 25; i++ { if err := c.Dele(i); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } } @@ -143,31 +142,31 @@ func TestPOP3(t *testing.T) { t.Log("Undeleting messages") if err := c.Rset(); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } if err := c.Quit(); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } c, err = connectAuth() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } count, _, err = c.Stat() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } assertEqual(t, count, 25, "incorrect message count") if err := c.Quit(); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } } @@ -190,7 +189,7 @@ func TestAuthentication(t *testing.T) { // non-authenticated connection c, err := connect() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -207,7 +206,7 @@ func TestAuthentication(t *testing.T) { } if err := c.Quit(); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -216,7 +215,7 @@ func TestAuthentication(t *testing.T) { // authenticated connection c, err = connectAuth() if err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } @@ -233,13 +232,15 @@ func TestAuthentication(t *testing.T) { } if err := c.Quit(); err != nil { - t.Errorf(err.Error()) + t.Error(err.Error()) return } } func setup() { - auth.SetPOP3Auth("username:password") + if err := auth.SetPOP3Auth("username:password"); err != nil { + panic(err) + } logger.NoLogging = true config.MaxMessages = 0 config.Database = os.Getenv("MP_DATABASE") diff --git a/internal/pop3/server.go b/internal/pop3/server.go index 19dc82a..454c3df 100644 --- a/internal/pop3/server.go +++ b/internal/pop3/server.go @@ -271,7 +271,7 @@ func handleTransactionCommand(conn net.Conn, cmd string, args []string, messages // begins with the termination octet, the line is "byte-stuffed" by // pre-pending the termination octet to that line of the response. // @see: https://www.ietf.org/rfc/rfc1939.txt - sendData(conn, strings.Replace(string(raw), "\n.", "\n..", -1)) + sendData(conn, strings.ReplaceAll(string(raw), "\n.", "\n..")) sendResponse(conn, ".") case "TOP": arg, err := getSafeArg(args, 0) diff --git a/internal/pop3client/client.go b/internal/pop3client/client.go index 0841782..a048cf1 100644 --- a/internal/pop3client/client.go +++ b/internal/pop3client/client.go @@ -422,7 +422,7 @@ func (c *Conn) Noop() error { // Message deletions (DELE command) are only executed by the server on a graceful // quit and close. func (c *Conn) Quit() error { - defer c.conn.Close() + defer func() { _ = c.conn.Close() }() if _, err := c.Cmd("QUIT", false); err != nil { return err diff --git a/internal/prometheus/metrics.go b/internal/prometheus/metrics.go index 1669613..e00738e 100644 --- a/internal/prometheus/metrics.go +++ b/internal/prometheus/metrics.go @@ -179,10 +179,10 @@ func StartSeparateServer() { func GetMode() string { mode := strings.ToLower(strings.TrimSpace(config.PrometheusListen)) - switch { - case mode == "false", mode == "": + switch mode { + case "false", "": return "disabled" - case mode == "true": + case "true": return "integrated" default: return "separate" diff --git a/internal/smtpd/forward.go b/internal/smtpd/forward.go index e4ec31c..7f7978c 100644 --- a/internal/smtpd/forward.go +++ b/internal/smtpd/forward.go @@ -37,7 +37,7 @@ func createForwardingSMTPClient(config config.SMTPForwardConfigStruct, addr stri client, err := smtp.NewClient(conn, tlsConf.ServerName) if err != nil { - conn.Close() + _ = conn.Close() return nil, fmt.Errorf("SMTP client error: %v", err) } @@ -55,7 +55,7 @@ func createForwardingSMTPClient(config config.SMTPForwardConfigStruct, addr stri tlsConf.InsecureSkipVerify = config.AllowInsecure if err = client.StartTLS(tlsConf); err != nil { - client.Close() + _ = client.Close() return nil, fmt.Errorf("error creating StartTLS config: %v", err) } } @@ -72,7 +72,7 @@ func forward(from string, msg []byte) error { if err != nil { return err } - defer c.Close() + defer func() { _ = c.Close() }() auth := forwardAuthFromConfig() diff --git a/internal/smtpd/relay.go b/internal/smtpd/relay.go index d1c979d..66c2829 100644 --- a/internal/smtpd/relay.go +++ b/internal/smtpd/relay.go @@ -71,7 +71,7 @@ func createRelaySMTPClient(config config.SMTPRelayConfigStruct, addr string) (*s client, err := smtp.NewClient(conn, tlsConf.ServerName) if err != nil { - conn.Close() + _ = conn.Close() return nil, fmt.Errorf("SMTP client error: %v", err) } @@ -89,7 +89,7 @@ func createRelaySMTPClient(config config.SMTPRelayConfigStruct, addr string) (*s tlsConf.InsecureSkipVerify = config.AllowInsecure if err = client.StartTLS(tlsConf); err != nil { - client.Close() + _ = client.Close() return nil, fmt.Errorf("error creating StartTLS config: %v", err) } } @@ -106,7 +106,7 @@ func Relay(from string, to []string, msg []byte) error { if err != nil { return err } - defer c.Close() + defer func() { _ = c.Close() }() auth := relayAuthFromConfig() @@ -193,7 +193,7 @@ func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) { case "Password:": return []byte(a.password), nil default: - return nil, errors.New("Unknown fromServer") + return nil, errors.New("unknown fromServer") } } diff --git a/internal/smtpd/smtpd.go b/internal/smtpd/smtpd.go index e936f71..29cb30b 100644 --- a/internal/smtpd/smtpd.go +++ b/internal/smtpd/smtpd.go @@ -217,7 +217,7 @@ func (srv *Server) Serve(ln net.Listener) error { return ErrServerClosed } - defer ln.Close() + defer func() { _ = ln.Close() }() for { // if we are shutting down, don't accept new connections @@ -229,7 +229,7 @@ func (srv *Server) Serve(ln net.Listener) error { conn, err := ln.Accept() if err != nil { - if netErr, ok := err.(net.Error); ok && netErr.Temporary() { + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { continue } return err @@ -356,7 +356,9 @@ func (srv *Server) Shutdown(ctx context.Context) error { // Function called to handle connection requests. func (s *session) serve() { defer atomic.AddInt32(&s.srv.openSessions, -1) - defer s.conn.Close() + // pass the connection into the defer function to ensure it is closed, + // otherwise results in a 5s timeout for each connection + defer func(c net.Conn) { _ = c.Close() }(s.conn) var from string var gotFrom bool @@ -517,9 +519,9 @@ loop: // On other errors, allow the client to try again. data, err := s.readData() if err != nil { - switch err.(type) { + switch err := err.(type) { case net.Error: - if err.(net.Error).Timeout() { + if err.Timeout() { s.writef("421 4.4.2 %s %s ESMTP Service closing transmission channel after timeout exceeded", s.srv.Hostname, s.srv.AppName) } break loop @@ -749,7 +751,7 @@ func (s *session) writef(format string, args ...interface{}) { } line := fmt.Sprintf(format, args...) - fmt.Fprintf(s.bw, "%s\r\n", line) + _, _ = fmt.Fprintf(s.bw, "%s\r\n", line) _ = s.bw.Flush() if Debug { diff --git a/internal/smtpd/smtpd_test.go b/internal/smtpd/smtpd_test.go index 2e17012..0f75482 100644 --- a/internal/smtpd/smtpd_test.go +++ b/internal/smtpd/smtpd_test.go @@ -12,7 +12,6 @@ import ( "fmt" "io" "net" - "os" "reflect" "regexp" "strings" @@ -40,7 +39,7 @@ func newConn(t *testing.T, server *Server) net.Conn { // Send a command and verify the 3 digit code from the response. func cmdCode(t *testing.T, conn net.Conn, cmd string, code string) string { - fmt.Fprintf(conn, "%s\r\n", cmd) + _, _ = fmt.Fprintf(conn, "%s\r\n", cmd) resp, err := bufio.NewReader(conn).ReadString('\n') if err != nil { t.Fatalf("Failed to read response from test server: %v", err) @@ -72,7 +71,9 @@ func TestSimpleCommands(t *testing.T) { conn := newConn(t, &Server{}) cmdCode(t, conn, tt.cmd, tt.code) cmdCode(t, conn, "QUIT", "221") - conn.Close() + if err := conn.Close(); err != nil { + t.Errorf("Failed to close connection after command %s: %v", tt.cmd, err) + } } } @@ -90,7 +91,7 @@ func TestCmdHELO(t *testing.T) { cmdCode(t, conn, "DATA", "503") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdEHLO(t *testing.T) { @@ -107,7 +108,7 @@ func TestCmdEHLO(t *testing.T) { cmdCode(t, conn, "DATA", "503") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdRSET(t *testing.T) { @@ -121,7 +122,7 @@ func TestCmdRSET(t *testing.T) { cmdCode(t, conn, "DATA", "503") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdMAIL(t *testing.T) { @@ -162,7 +163,7 @@ func TestCmdMAIL(t *testing.T) { // TODO: MAIL with invalid AUTH parameter must return 501 syntax error cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdMAILMaxSize(t *testing.T) { @@ -192,7 +193,7 @@ func TestCmdMAILMaxSize(t *testing.T) { // Clients should send either RSET or QUIT after receiving 552 (RFC 1870 section 6.2). cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdRCPT(t *testing.T) { @@ -239,7 +240,7 @@ func TestCmdRCPT(t *testing.T) { cmdCode(t, conn, "RCPT TO: ", "501") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdMaxRecipients(t *testing.T) { @@ -256,7 +257,7 @@ func TestCmdMaxRecipients(t *testing.T) { cmdCode(t, conn, "RCPT TO: ", "452") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdDATA(t *testing.T) { @@ -286,7 +287,7 @@ func TestCmdDATA(t *testing.T) { cmdCode(t, conn, "Test message.\r\n.", "250") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdDATAWithMaxSize(t *testing.T) { @@ -323,7 +324,7 @@ func TestCmdDATAWithMaxSize(t *testing.T) { // Clients should send either RSET or QUIT after receiving 552 (RFC 1870 section 6.2). cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } type mockHandler struct { @@ -347,7 +348,7 @@ func TestCmdDATAWithHandler(t *testing.T) { cmdCode(t, conn, "DATA", "354") cmdCode(t, conn, "Test message.\r\n.", "250") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() if m.handlerCalled != 1 { t.Errorf("MailHandler called %d times, want one call", m.handlerCalled) @@ -364,7 +365,7 @@ func TestCmdDATAWithHandlerError(t *testing.T) { cmdCode(t, conn, "DATA", "354") cmdCode(t, conn, "Test message.\r\n.", "451") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() if m.handlerCalled != 1 { t.Errorf("MailHandler called %d times, want one call", m.handlerCalled) @@ -382,7 +383,7 @@ func TestCmdSTARTTLS(t *testing.T) { cmdCode(t, conn, "STARTTLS FOO", "501") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdSTARTTLSFailure(t *testing.T) { @@ -411,7 +412,7 @@ func TestCmdSTARTTLSFailure(t *testing.T) { } cmdCode(t, conn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } // Utility function to make a valid TLS certificate for use by the server. @@ -497,7 +498,7 @@ func TestCmdSTARTTLSSuccess(t *testing.T) { cmdCode(t, tlsConn, "STARTTLS", "503") cmdCode(t, tlsConn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } func TestCmdSTARTTLSRequired(t *testing.T) { @@ -548,7 +549,7 @@ func TestCmdSTARTTLSRequired(t *testing.T) { } cmdCode(t, tlsConn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } func TestMakeHeaders(t *testing.T) { @@ -798,8 +799,8 @@ func TestMakeEHLOResponse(t *testing.T) { t.Errorf("AUTH does not appear in the extension list when an AuthHandler is specified") } - reLogin := regexp.MustCompile("\\bLOGIN\\b") - rePlain := regexp.MustCompile("\\bPLAIN\\b") + reLogin := regexp.MustCompile(`\bLOGIN\b`) + rePlain := regexp.MustCompile(`\bPLAIN\b`) // RFC 4954 specifies that, without TLS in use, plaintext authentication mechanisms must not be advertised. s.tls = false @@ -822,86 +823,86 @@ func TestMakeEHLOResponse(t *testing.T) { } } -func createTmpFile(content string) (file *os.File, err error) { - file, err = os.CreateTemp("", "") - if err != nil { - return - } - _, err = file.Write([]byte(content)) - if err != nil { - return - } - err = file.Close() - return -} +// func createTmpFile(content string) (file *os.File, err error) { +// file, err = os.CreateTemp("", "") +// if err != nil { +// return +// } +// _, err = file.Write([]byte(content)) +// if err != nil { +// return +// } +// err = file.Close() +// return +// } -func createTLSFiles() ( - certFile *os.File, - keyFile *os.File, - passphrase string, - err error, -) { - const certPEM = `-----BEGIN CERTIFICATE----- -MIIDRzCCAi+gAwIBAgIJAKtg4oViVwv4MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV -BAMMCWxvY2FsaG9zdDAgFw0xODA0MjAxMzMxNTBaGA8yMDg2MDUwODEzMzE1MFow -FDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA8h7vl0gUquis5jRtcnETyD+8WITZO0s53aIzp0Y+9HXiHW6FGJjbOZjM -IvozNVni+83QWKumRTgeSzIIW2j4V8iFMSNrvWmhmCKloesXS1aY6H979e01Ve8J -WAJFRe6vZJd6gC6Z/P+ELU3ie4Vtr1GYfkV7nZ6VFp5/V/5nxGFag5TUlpP5hcoS -9r2kvXofosVwe3x3udT8SEbv5eBD4bKeVyJs/RLbxSuiU1358Y1cDdVuHjcvfm3c -ajhheQ4vX9WXsk7LGGhnf1SrrPN/y+IDTXfvoHn+nJh4vMAB4yzQdE1V1N1AB8RA -0yBVJ6dwxRrSg4BFrNWhj3gfsvrA7wIDAQABo4GZMIGWMB0GA1UdDgQWBBQ4/ncp -befFuKH1hoYkPqLwuRrPRjAfBgNVHSMEGDAWgBQ4/ncpbefFuKH1hoYkPqLwuRrP -RjAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDALBgNVHQ8EBAMCBaAwEwYD -VR0lBAwwCgYIKwYBBQUHAwEwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3 -DQEBCwUAA4IBAQBJBetEXiEIzKAEpXGX87j6aUON51Fdf6BiLMCghuGKyhnaOG32 -4KJhtvVoS3ZUKPylh9c2VdItYlhWp76zd7YKk+3xUOixWeTMQHIvCvRGTyFibOPT -mApwp2pEnJCe4vjUrBaRhiyI+xnB70cWVF2qeernlLUeJA1mfYyQLz+v06ebDWOL -c/hPVQFB94lEdiyjGO7RZfIe8KwcK48g7iv0LQU4+c9MoWM2ZsVM1AL2tHzokSeA -u64gDTW4K0Tzx1ab7KmOFXYUjbz/xWuReMt33EwDXAErKCjbVt2T55Qx8UoKzSh1 -tY0KDHdnYOzgsm2HIj2xcJqbeylYQvckNnoC ------END CERTIFICATE-----` +// func createTLSFiles() ( +// certFile *os.File, +// keyFile *os.File, +// passphrase string, +// err error, +// ) { +// const certPEM = `-----BEGIN CERTIFICATE----- +// MIIDRzCCAi+gAwIBAgIJAKtg4oViVwv4MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV +// BAMMCWxvY2FsaG9zdDAgFw0xODA0MjAxMzMxNTBaGA8yMDg2MDUwODEzMzE1MFow +// FDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +// CgKCAQEA8h7vl0gUquis5jRtcnETyD+8WITZO0s53aIzp0Y+9HXiHW6FGJjbOZjM +// IvozNVni+83QWKumRTgeSzIIW2j4V8iFMSNrvWmhmCKloesXS1aY6H979e01Ve8J +// WAJFRe6vZJd6gC6Z/P+ELU3ie4Vtr1GYfkV7nZ6VFp5/V/5nxGFag5TUlpP5hcoS +// 9r2kvXofosVwe3x3udT8SEbv5eBD4bKeVyJs/RLbxSuiU1358Y1cDdVuHjcvfm3c +// ajhheQ4vX9WXsk7LGGhnf1SrrPN/y+IDTXfvoHn+nJh4vMAB4yzQdE1V1N1AB8RA +// 0yBVJ6dwxRrSg4BFrNWhj3gfsvrA7wIDAQABo4GZMIGWMB0GA1UdDgQWBBQ4/ncp +// befFuKH1hoYkPqLwuRrPRjAfBgNVHSMEGDAWgBQ4/ncpbefFuKH1hoYkPqLwuRrP +// RjAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDALBgNVHQ8EBAMCBaAwEwYD +// VR0lBAwwCgYIKwYBBQUHAwEwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3 +// DQEBCwUAA4IBAQBJBetEXiEIzKAEpXGX87j6aUON51Fdf6BiLMCghuGKyhnaOG32 +// 4KJhtvVoS3ZUKPylh9c2VdItYlhWp76zd7YKk+3xUOixWeTMQHIvCvRGTyFibOPT +// mApwp2pEnJCe4vjUrBaRhiyI+xnB70cWVF2qeernlLUeJA1mfYyQLz+v06ebDWOL +// c/hPVQFB94lEdiyjGO7RZfIe8KwcK48g7iv0LQU4+c9MoWM2ZsVM1AL2tHzokSeA +// u64gDTW4K0Tzx1ab7KmOFXYUjbz/xWuReMt33EwDXAErKCjbVt2T55Qx8UoKzSh1 +// tY0KDHdnYOzgsm2HIj2xcJqbeylYQvckNnoC +// -----END CERTIFICATE-----` - const keyPEM = `-----BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-256-CBC,C16BF8745B2CDB53AC2B1D7609893AA0 +// const keyPEM = `-----BEGIN RSA PRIVATE KEY----- +// Proc-Type: 4,ENCRYPTED +// DEK-Info: AES-256-CBC,C16BF8745B2CDB53AC2B1D7609893AA0 -O13z7Yq7butaJmMfg9wRis9YnIDPsp4coYI6Ud+JGcP7iXoy95QMhovKWx25o1ol -tvUTsrsG27fHGf9qG02KizApIVtO9c1e0swCWzFrKRQX0JDiZDmilb9xosBNNst1 -BOzOTRZEwFGSOCKZRBfSXyqC93TvLJ3DO9IUnKIeGt7upipvg29b/Dur/fyCy2WV -bLHXwUTDBm7j49yfoEyGkDjoB2QO9wgcgbacbnQJQ25fTFUwZpZJEJv6o1tRhoYM -ZMOhC9x1URmdHKN1+z2y5BrB6oNpParfeAMEvs/9FE6jJwYUR28Ql6Mhphfvr9W2 -5Gxd3J65Ao9Vi2I5j5X6aBuNjyhXN3ScLjPG4lVZm9RU/uTPEt81pig/d5nSAjvF -Nfc08NuG3cnMyJSE/xScJ4D+GtX8U969wO4oKPCR4E/NFyXPR730ppupDFG6hzPD -PDmiszDtU438JAZ8AuFa1LkbyFnEW6KVD4h7VRr8YDjirCqnkgjNSI6dFY0NQ8H7 -SyexB0lrceX6HZc+oNdAtkX3tYdzY3ExzUM5lSF1dkldnRbApLbqc4uuNIVXhXFM -dJnoPdKAzM6i+2EeVUxWNdafKDxnjVSHIHzHfIFJLQ4GS5rnz9keRFdyDjQL07tT -Lu9pPOmsadDXp7oSa81RgoCUfNZeR4jKpCk2BOft0L6ZSqwYFLcQHLIfJaGfn902 -TUOTxHt0KzEUYeYSrXC2a6cyvXAd1YI7lOgy60qG89VHyCc2v5Bs4c4FNUDC/+Dj -4ZwogaAbSNkLaE0q3sYQRPdxSqLftyX0KitAgE7oGtdzBfe1cdBoozw3U67NEMMT -6qvk5j7RepPRSrapHtK5pMMdg5XpKFWcOXZ26VHVrDCj4JKdjVb4iyiQi94VveV0 -w9+KcOtyrM7/jbQlCWnXpsIkP8VA/RIgh7CBn/h4oF1sO8ywP25OGQ7VWAVq1R9D -8bl8GzIdR9PZpFyOxuIac4rPa8tkDeoXKs4cxoao7H/OZO9o9aTB7CJMTL9yv0Kb -ntWuYxQchE6syoGsOgdGyZhaw4JeFkasDUP5beyNY+278NkzgGTOIMMTXIX46woP -ehzHKGHXVGf7ZiSFF+zAHMXZRSwNVMkOYwlIoRg1IbvIRbAXqAR6xXQTCVzNG0SU -cskojycBca1Cz3hDVIKYZd9beDhprVdr2a4K2nft2g2xRNjKPopsaqXx+VPibFUx -X7542eQ3eAlhkWUuXvt0q5a9WJdjJp9ODA0/d0akF6JQlEHIAyLfoUKB1HYwgUGG -6uRm651FDAab9U4cVC5PY1hfv/QwzpkNDkzgJAZ5SMOfZhq7IdBcqGd3lzPmq2FP -Vy1LVZIl3eM+9uJx5TLsBHH6NhMwtNhFCNa/5ksodQYlTvR8IrrgWlYg4EL69vjS -yt6HhhEN3lFCWvrQXQMp93UklbTlpVt6qcDXiC7HYbs3+EINargRd5Z+xL5i5vkN -f9k7s0xqhloWNPZcyOXMrox8L81WOY+sP4mVlGcfDRLdEJ8X2ofJpOAcwYCnjsKd -uEGsi+l2fTj/F+eZLE6sYoMprgJrbfeqtRWFguUgTn7s5hfU0tZ46al5d0vz8fWK ------END RSA PRIVATE KEY-----` +// O13z7Yq7butaJmMfg9wRis9YnIDPsp4coYI6Ud+JGcP7iXoy95QMhovKWx25o1ol +// tvUTsrsG27fHGf9qG02KizApIVtO9c1e0swCWzFrKRQX0JDiZDmilb9xosBNNst1 +// BOzOTRZEwFGSOCKZRBfSXyqC93TvLJ3DO9IUnKIeGt7upipvg29b/Dur/fyCy2WV +// bLHXwUTDBm7j49yfoEyGkDjoB2QO9wgcgbacbnQJQ25fTFUwZpZJEJv6o1tRhoYM +// ZMOhC9x1URmdHKN1+z2y5BrB6oNpParfeAMEvs/9FE6jJwYUR28Ql6Mhphfvr9W2 +// 5Gxd3J65Ao9Vi2I5j5X6aBuNjyhXN3ScLjPG4lVZm9RU/uTPEt81pig/d5nSAjvF +// Nfc08NuG3cnMyJSE/xScJ4D+GtX8U969wO4oKPCR4E/NFyXPR730ppupDFG6hzPD +// PDmiszDtU438JAZ8AuFa1LkbyFnEW6KVD4h7VRr8YDjirCqnkgjNSI6dFY0NQ8H7 +// SyexB0lrceX6HZc+oNdAtkX3tYdzY3ExzUM5lSF1dkldnRbApLbqc4uuNIVXhXFM +// dJnoPdKAzM6i+2EeVUxWNdafKDxnjVSHIHzHfIFJLQ4GS5rnz9keRFdyDjQL07tT +// Lu9pPOmsadDXp7oSa81RgoCUfNZeR4jKpCk2BOft0L6ZSqwYFLcQHLIfJaGfn902 +// TUOTxHt0KzEUYeYSrXC2a6cyvXAd1YI7lOgy60qG89VHyCc2v5Bs4c4FNUDC/+Dj +// 4ZwogaAbSNkLaE0q3sYQRPdxSqLftyX0KitAgE7oGtdzBfe1cdBoozw3U67NEMMT +// 6qvk5j7RepPRSrapHtK5pMMdg5XpKFWcOXZ26VHVrDCj4JKdjVb4iyiQi94VveV0 +// w9+KcOtyrM7/jbQlCWnXpsIkP8VA/RIgh7CBn/h4oF1sO8ywP25OGQ7VWAVq1R9D +// 8bl8GzIdR9PZpFyOxuIac4rPa8tkDeoXKs4cxoao7H/OZO9o9aTB7CJMTL9yv0Kb +// ntWuYxQchE6syoGsOgdGyZhaw4JeFkasDUP5beyNY+278NkzgGTOIMMTXIX46woP +// ehzHKGHXVGf7ZiSFF+zAHMXZRSwNVMkOYwlIoRg1IbvIRbAXqAR6xXQTCVzNG0SU +// cskojycBca1Cz3hDVIKYZd9beDhprVdr2a4K2nft2g2xRNjKPopsaqXx+VPibFUx +// X7542eQ3eAlhkWUuXvt0q5a9WJdjJp9ODA0/d0akF6JQlEHIAyLfoUKB1HYwgUGG +// 6uRm651FDAab9U4cVC5PY1hfv/QwzpkNDkzgJAZ5SMOfZhq7IdBcqGd3lzPmq2FP +// Vy1LVZIl3eM+9uJx5TLsBHH6NhMwtNhFCNa/5ksodQYlTvR8IrrgWlYg4EL69vjS +// yt6HhhEN3lFCWvrQXQMp93UklbTlpVt6qcDXiC7HYbs3+EINargRd5Z+xL5i5vkN +// f9k7s0xqhloWNPZcyOXMrox8L81WOY+sP4mVlGcfDRLdEJ8X2ofJpOAcwYCnjsKd +// uEGsi+l2fTj/F+eZLE6sYoMprgJrbfeqtRWFguUgTn7s5hfU0tZ46al5d0vz8fWK +// -----END RSA PRIVATE KEY-----` - passphrase = "test" +// passphrase = "test" - certFile, err = createTmpFile(certPEM) - if err != nil { - return - } - keyFile, err = createTmpFile(keyPEM) - return -} +// certFile, err = createTmpFile(certPEM) +// if err != nil { +// return +// } +// keyFile, err = createTmpFile(keyPEM) +// return +// } func TestAuthMechs(t *testing.T) { s := session{} @@ -966,7 +967,7 @@ func TestCmdAUTH(t *testing.T) { cmdCode(t, conn, "AUTH", "502") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdAUTHOptional(t *testing.T) { @@ -1007,7 +1008,7 @@ func TestCmdAUTHOptional(t *testing.T) { cmdCode(t, conn, "*", "501") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdAUTHRequired(t *testing.T) { @@ -1056,7 +1057,7 @@ func TestCmdAUTHRequired(t *testing.T) { cmdCode(t, conn, "AUTH PLAIN", "504") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdAUTHLOGIN(t *testing.T) { @@ -1113,7 +1114,7 @@ func TestCmdAUTHLOGIN(t *testing.T) { cmdCode(t, tlsConn, "AUTH CRAM-MD5", "503") cmdCode(t, tlsConn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } func TestCmdAUTHLOGINFast(t *testing.T) { @@ -1165,7 +1166,7 @@ func TestCmdAUTHLOGINFast(t *testing.T) { cmdCode(t, tlsConn, "AUTH CRAM-MD5", "503") cmdCode(t, tlsConn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } func TestCmdAUTHPLAIN(t *testing.T) { @@ -1224,7 +1225,7 @@ func TestCmdAUTHPLAIN(t *testing.T) { cmdCode(t, tlsConn, "AUTH CRAM-MD5", "503") cmdCode(t, tlsConn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } func TestCmdAUTHPLAINEmpty(t *testing.T) { @@ -1283,7 +1284,7 @@ func TestCmdAUTHPLAINEmpty(t *testing.T) { cmdCode(t, tlsConn, "AUTH CRAM-MD5", "503") cmdCode(t, tlsConn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } func TestCmdAUTHPLAINFast(t *testing.T) { @@ -1335,7 +1336,7 @@ func TestCmdAUTHPLAINFast(t *testing.T) { cmdCode(t, tlsConn, "AUTH CRAM-MD5", "503") cmdCode(t, tlsConn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } func TestCmdAUTHPLAINFastAndEmpty(t *testing.T) { @@ -1387,7 +1388,7 @@ func TestCmdAUTHPLAINFastAndEmpty(t *testing.T) { cmdCode(t, tlsConn, "AUTH CRAM-MD5", "503") cmdCode(t, tlsConn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } // makeCRAMMD5Response is a helper function to create the CRAM-MD5 hash. @@ -1457,7 +1458,7 @@ func TestCmdAUTHCRAMMD5(t *testing.T) { cmdCode(t, conn, "AUTH CRAM-MD5", "503") cmdCode(t, conn, "QUIT", "221") - conn.Close() + _ = conn.Close() } func TestCmdAUTHCRAMMD5WithTLS(t *testing.T) { @@ -1523,7 +1524,7 @@ func TestCmdAUTHCRAMMD5WithTLS(t *testing.T) { cmdCode(t, tlsConn, "AUTH CRAM-MD5", "503") cmdCode(t, tlsConn, "QUIT", "221") - tlsConn.Close() + _ = tlsConn.Close() } // Benchmark the mail handling without the network stack introducing latency. @@ -1540,17 +1541,17 @@ func BenchmarkReceive(b *testing.B) { // Benchmark a full mail transaction. for i := 0; i < b.N; i++ { - fmt.Fprintf(clientConn, "%s\r\n", "HELO host.example.com") + _, _ = fmt.Fprintf(clientConn, "%s\r\n", "HELO host.example.com") _, _ = reader.ReadString('\n') - fmt.Fprintf(clientConn, "%s\r\n", "MAIL FROM:") + _, _ = fmt.Fprintf(clientConn, "%s\r\n", "MAIL FROM:") _, _ = reader.ReadString('\n') - fmt.Fprintf(clientConn, "%s\r\n", "RCPT TO:") + _, _ = fmt.Fprintf(clientConn, "%s\r\n", "RCPT TO:") _, _ = reader.ReadString('\n') - fmt.Fprintf(clientConn, "%s\r\n", "DATA") + _, _ = fmt.Fprintf(clientConn, "%s\r\n", "DATA") _, _ = reader.ReadString('\n') - fmt.Fprintf(clientConn, "%s\r\n", "Test message.\r\n.") + _, _ = fmt.Fprintf(clientConn, "%s\r\n", "Test message.\r\n.") _, _ = reader.ReadString('\n') - fmt.Fprintf(clientConn, "%s\r\n", "QUIT") + _, _ = fmt.Fprintf(clientConn, "%s\r\n", "QUIT") _, _ = reader.ReadString('\n') } } @@ -1589,11 +1590,11 @@ func TestCmdShutdown(t *testing.T) { cmdCode(t, conn, "QUIT", "221") // connection should now be closed - fmt.Fprintf(conn, "%s\r\n", "HELO host.example.com") + _, _ = fmt.Fprintf(conn, "%s\r\n", "HELO host.example.com") _, err := bufio.NewReader(conn).ReadString('\n') if err != io.EOF { t.Errorf("Expected connection to be closed\n") } - conn.Close() + _ = conn.Close() } diff --git a/internal/spamassassin/postmark/postmark.go b/internal/spamassassin/postmark/postmark.go index 854a232..6388844 100644 --- a/internal/spamassassin/postmark/postmark.go +++ b/internal/spamassassin/postmark/postmark.go @@ -59,7 +59,7 @@ func Check(email []byte, timeout int) (Response, error) { return r, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() err = json.NewDecoder(resp.Body).Decode(&r) diff --git a/internal/spamassassin/spamc/spamc.go b/internal/spamassassin/spamc/spamc.go index 0f51023..32f20bb 100644 --- a/internal/spamassassin/spamc/spamc.go +++ b/internal/spamassassin/spamc/spamc.go @@ -70,21 +70,22 @@ type Result struct { // dial connects to spamd through TCP or a Unix socket. func (c *Client) dial() (connection, error) { - if c.net == "tcp" { + switch c.net { + case "tcp": tcpAddr, err := net.ResolveTCPAddr("tcp", c.addr) if err != nil { return nil, err } return net.DialTCP("tcp", nil, tcpAddr) - } else if c.net == "unix" { + case "unix": unixAddr, err := net.ResolveUnixAddr("unix", c.addr) if err != nil { return nil, err } return net.DialUnix("unix", nil, unixAddr) + default: + return nil, fmt.Errorf("unsupported network type: %s", c.net) } - - panic("Client.net must be either \"tcp\" or \"unix\"") } // Report checks if message is spam or not, and returns score plus report @@ -103,7 +104,7 @@ func (c *Client) report(email []byte) ([]string, error) { return nil, err } - defer conn.Close() + defer func() { _ = conn.Close() }() if err := conn.SetDeadline(time.Now().Add(time.Duration(c.timeout) * time.Second)); err != nil { return nil, err @@ -221,7 +222,7 @@ func (c *Client) Ping() error { if err != nil { return err } - defer conn.Close() + defer func() { _ = conn.Close() }() if err := conn.SetDeadline(time.Now().Add(time.Duration(c.timeout) * time.Second)); err != nil { return err diff --git a/internal/storage/database.go b/internal/storage/database.go index 73bd446..be7848d 100644 --- a/internal/storage/database.go +++ b/internal/storage/database.go @@ -27,7 +27,6 @@ import ( var ( db *sql.DB - dbFile string sqlDriver string dbLastAction time.Time @@ -139,7 +138,6 @@ func InitDB() error { LoadTagFilters() - dbFile = p dbLastAction = time.Now() sigs := make(chan os.Signal, 1) diff --git a/internal/storage/messages.go b/internal/storage/messages.go index 8e636ce..2ead654 100644 --- a/internal/storage/messages.go +++ b/internal/storage/messages.go @@ -86,7 +86,7 @@ func Store(body *[]byte, username *string) (string, error) { } // roll back if it fails - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() subject := env.GetHeader("Subject") size := uint64(len(*body)) @@ -118,8 +118,6 @@ func Store(body *[]byte, username *string) (string, error) { } else { _, err = tx.Exec(fmt.Sprintf(`INSERT INTO %s (ID, Email, Compressed) VALUES(?, ?, 1)`, tenant("mailbox_data")), id, compressed) // #nosec } - - compressed = nil } else { // insert uncompressed raw message _, err = tx.Exec(fmt.Sprintf(`INSERT INTO %s (ID, Email, Compressed) VALUES(?, ?, 0)`, tenant("mailbox_data")), id, string(*body)) // #nosec @@ -639,7 +637,7 @@ func DeleteMessages(ids []string) error { if err != nil { return err } - defer rows.Close() + defer func() { _ = rows.Close() }() toDelete := []string{} var totalSize uint64 @@ -738,7 +736,7 @@ func DeleteAllMessages() error { } // roll back if it fails - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() tables := []string{"mailbox", "mailbox_data", "tags", "message_tags"} diff --git a/internal/storage/reindex.go b/internal/storage/reindex.go index cee8007..acc3aa9 100644 --- a/internal/storage/reindex.go +++ b/internal/storage/reindex.go @@ -114,7 +114,7 @@ func ReindexAll() { } // roll back if it fails - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() // insert mail summary data for _, u := range updates { diff --git a/internal/storage/search.go b/internal/storage/search.go index ffd2c06..252b4c9 100644 --- a/internal/storage/search.go +++ b/internal/storage/search.go @@ -193,7 +193,7 @@ func DeleteSearch(search, timezone string) error { } // roll back if it fails - defer tx.Rollback() + defer func() { _ = tx.Rollback() }() for _, ids := range chunks { delIDs := make([]interface{}, len(ids)) diff --git a/internal/tools/fs.go b/internal/tools/fs.go index 411b02e..0867ce2 100644 --- a/internal/tools/fs.go +++ b/internal/tools/fs.go @@ -8,7 +8,7 @@ import ( // IsFile returns whether a file exists and is readable func IsFile(path string) bool { f, err := os.Open(filepath.Clean(path)) - defer f.Close() + defer func() { _ = f.Close() }() return err == nil } diff --git a/internal/tools/listunsubscribeparser.go b/internal/tools/listunsubscribeparser.go index ee5e573..1d64a0e 100644 --- a/internal/tools/listunsubscribeparser.go +++ b/internal/tools/listunsubscribeparser.go @@ -27,7 +27,7 @@ func ListUnsubscribeParser(v string) ([]string, error) { comments := reComments.FindAllStringSubmatch(v, -1) for _, c := range comments { // strip comments - v = strings.Replace(v, c[0], "", -1) + v = strings.ReplaceAll(v, c[0], "") v = strings.TrimSpace(v) } diff --git a/internal/updater/targz.go b/internal/updater/targz.go index 78e1539..b138d1c 100644 --- a/internal/updater/targz.go +++ b/internal/updater/targz.go @@ -115,7 +115,7 @@ func extract(filePath string, directory string) error { if err != nil { return err } - defer gzipReader.Close() + defer func() { _ = gzipReader.Close() }() tarReader := tar.NewReader(gzipReader) diff --git a/internal/updater/unzip.go b/internal/updater/unzip.go index d811fd3..3fc2d57 100644 --- a/internal/updater/unzip.go +++ b/internal/updater/unzip.go @@ -19,7 +19,7 @@ func Unzip(src string, dest string) ([]string, error) { if err != nil { return filenames, err } - defer r.Close() + defer func() { _ = r.Close() }() for _, f := range r.File { diff --git a/internal/updater/updater.go b/internal/updater/updater.go index 9a66437..7f6bf27 100644 --- a/internal/updater/updater.go +++ b/internal/updater/updater.go @@ -5,6 +5,7 @@ import ( "crypto/rand" "encoding/hex" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -70,7 +71,7 @@ func GithubLatest(repo, name string) (string, string, string, error) { return "", "", "", err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(resp.Body) @@ -119,7 +120,7 @@ func GithubLatest(repo, name string) (string, string, string, error) { if len(allReleases) == 0 { // no releases with suitable assets found - return "", "", "", fmt.Errorf("No binary releases found") + return "", "", "", errors.New("no binary releases found") } var latestRelease = Release{} @@ -149,11 +150,11 @@ func GithubUpdate(repo, appName, currentVersion string) (string, error) { } if ver == currentVersion { - return "", fmt.Errorf("No new release found") + return "", errors.New("no new release found") } if semver.Compare(ver, currentVersion) < 1 { - return "", fmt.Errorf("No newer releases found (latest %s)", ver) + return "", fmt.Errorf("no newer releases found (latest %s)", ver) } tmpDir := getTempDir() @@ -212,7 +213,7 @@ func downloadToFile(url, fileName string) error { if err != nil { return err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() // Create the file out, err := os.Create(filepath.Clean(fileName)) diff --git a/sendmail/cmd/cmd.go b/sendmail/cmd/cmd.go index ee40f6e..5fbad55 100644 --- a/sendmail/cmd/cmd.go +++ b/sendmail/cmd/cmd.go @@ -121,14 +121,14 @@ func Run() { // handles `sendmail -bs` // telnet directly to SMTP if UseB && UseS { - var caller telnet.Caller = telnet.StandardCaller - - if isSocket { + var caller = telnet.StandardCaller + switch isSocket { + case true: if err := telnet.DialToAndCallUnix(socketAddr, caller); err != nil { fmt.Println(err) os.Exit(1) } - } else { + default: if err := telnet.DialToAndCall(SMTPAddr, caller); err != nil { fmt.Println(err) os.Exit(1) diff --git a/server/apiv1/api.go b/server/apiv1/api.go index 124819c..1008f14 100644 --- a/server/apiv1/api.go +++ b/server/apiv1/api.go @@ -18,7 +18,7 @@ func fourOFour(w http.ResponseWriter) { w.Header().Set("Content-Security-Policy", config.ContentSecurityPolicy) w.WriteHeader(http.StatusNotFound) w.Header().Set("Content-Type", "text/plain") - fmt.Fprint(w, "404 page not found") + _, _ = fmt.Fprint(w, "404 page not found") } // HTTPError returns a basic error message (400 response) @@ -27,7 +27,7 @@ func httpError(w http.ResponseWriter, msg string) { w.Header().Set("Content-Security-Policy", config.ContentSecurityPolicy) w.WriteHeader(http.StatusBadRequest) w.Header().Set("Content-Type", "text/plain") - fmt.Fprint(w, msg) + _, _ = fmt.Fprint(w, msg) } // httpJSONError returns a basic error message (400 response) in JSON format diff --git a/server/apiv1/message.go b/server/apiv1/message.go index beca33c..455876b 100644 --- a/server/apiv1/message.go +++ b/server/apiv1/message.go @@ -49,7 +49,7 @@ func GetMessage(w http.ResponseWriter, r *http.Request) { id, err = storage.LatestID(r) if err != nil { w.WriteHeader(404) - fmt.Fprint(w, err.Error()) + _, _ = fmt.Fprint(w, err.Error()) return } } @@ -108,7 +108,7 @@ func GetHeaders(w http.ResponseWriter, r *http.Request) { id, err = storage.LatestID(r) if err != nil { w.WriteHeader(404) - fmt.Fprint(w, err.Error()) + _, _ = fmt.Fprint(w, err.Error()) return } } @@ -179,7 +179,7 @@ func DownloadAttachment(w http.ResponseWriter, r *http.Request) { id, err = storage.LatestID(r) if err != nil { w.WriteHeader(404) - fmt.Fprint(w, err.Error()) + _, _ = fmt.Fprint(w, err.Error()) return } } @@ -238,7 +238,7 @@ func DownloadRaw(w http.ResponseWriter, r *http.Request) { id, err = storage.LatestID(r) if err != nil { w.WriteHeader(404) - fmt.Fprint(w, err.Error()) + _, _ = fmt.Fprint(w, err.Error()) return } } diff --git a/server/apiv1/other.go b/server/apiv1/other.go index 3a24461..ed77ff9 100644 --- a/server/apiv1/other.go +++ b/server/apiv1/other.go @@ -210,7 +210,7 @@ func SpamAssassinCheck(w http.ResponseWriter, r *http.Request) { id, err = storage.LatestID(r) if err != nil { w.WriteHeader(404) - fmt.Fprint(w, err.Error()) + _, _ = fmt.Fprint(w, err.Error()) return } } diff --git a/server/apiv1/testing.go b/server/apiv1/testing.go index 968bfc7..80db613 100644 --- a/server/apiv1/testing.go +++ b/server/apiv1/testing.go @@ -67,7 +67,7 @@ func GetMessageHTML(w http.ResponseWriter, r *http.Request) { id, err = storage.LatestID(r) if err != nil { w.WriteHeader(404) - fmt.Fprint(w, err.Error()) + _, _ = fmt.Fprint(w, err.Error()) return } } @@ -75,12 +75,12 @@ func GetMessageHTML(w http.ResponseWriter, r *http.Request) { msg, err := storage.GetMessage(id) if err != nil { w.WriteHeader(404) - fmt.Fprint(w, "Message not found") + _, _ = fmt.Fprint(w, "Message not found") return } if msg.HTML == "" { w.WriteHeader(404) - fmt.Fprint(w, "This message does not contain a HTML part") + _, _ = fmt.Fprint(w, "This message does not contain a HTML part") return } @@ -161,7 +161,7 @@ func GetMessageText(w http.ResponseWriter, r *http.Request) { id, err = storage.LatestID(r) if err != nil { w.WriteHeader(404) - fmt.Fprint(w, err.Error()) + _, _ = fmt.Fprint(w, err.Error()) return } } @@ -169,7 +169,7 @@ func GetMessageText(w http.ResponseWriter, r *http.Request) { msg, err := storage.GetMessage(id) if err != nil { w.WriteHeader(404) - fmt.Fprint(w, "Message not found") + _, _ = fmt.Fprint(w, "Message not found") return } diff --git a/server/handlers/messages.go b/server/handlers/messages.go index 2076604..4d03035 100644 --- a/server/handlers/messages.go +++ b/server/handlers/messages.go @@ -39,5 +39,5 @@ func RedirectToLatestMessage(w http.ResponseWriter, r *http.Request) { } } - http.Redirect(w, r, uri, 302) + http.Redirect(w, r, uri, http.StatusFound) } diff --git a/server/handlers/proxy.go b/server/handlers/proxy.go index 33557c6..5b5b0dc 100644 --- a/server/handlers/proxy.go +++ b/server/handlers/proxy.go @@ -60,7 +60,8 @@ func ProxyHandler(w http.ResponseWriter, r *http.Request) { return } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() + body, err := io.ReadAll(resp.Body) if err != nil { logger.Log().Warnf("[proxy] %s", err.Error()) @@ -141,7 +142,7 @@ func absoluteURL(link, baseURL string) (string, error) { // ensure link is HTTP(S) if result.Scheme != "http" && result.Scheme != "https" { - return link, fmt.Errorf("Invalid URL: %s", result.String()) + return link, fmt.Errorf("invalid URL: %s", result.String()) } return result.String(), nil @@ -153,5 +154,5 @@ func httpError(w http.ResponseWriter, msg string) { w.Header().Set("Content-Security-Policy", config.ContentSecurityPolicy) w.WriteHeader(http.StatusBadRequest) w.Header().Set("Content-Type", "text/plain") - fmt.Fprint(w, msg) + _, _ = fmt.Fprint(w, msg) } diff --git a/server/server.go b/server/server.go index d2c2b84..78b525f 100644 --- a/server/server.go +++ b/server/server.go @@ -290,8 +290,9 @@ func middleWareFunc(fn http.HandlerFunc) http.HandlerFunc { } // Check basic authentication headers if configured. - // OPTIONS requests are skipped if CORS is enabled, since browsers omit credentials for preflight. - if !(AccessControlAllowOrigin != "" && r.Method == http.MethodOptions) && auth.UICredentials != nil { + // OPTIONS requests are skipped if CORS is enabled, since browsers omit credentials for preflight checks. + isCORSOptionsRequest := AccessControlAllowOrigin != "" && r.Method == http.MethodOptions + if !isCORSOptionsRequest && auth.UICredentials != nil { user, pass, ok := r.BasicAuth() if !ok { @@ -312,7 +313,7 @@ func middleWareFunc(fn http.HandlerFunc) http.HandlerFunc { w.Header().Set("Content-Encoding", "gzip") gz := gzip.NewWriter(w) - defer gz.Close() + defer func() { _ = gz.Close() }() gzr := gzipResponseWriter{Writer: gz, ResponseWriter: w} fn(gzr, r) } diff --git a/server/server_test.go b/server/server_test.go index f8d3ec8..ccc3799 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -345,7 +345,9 @@ func TestSendAPIAuthMiddleware(t *testing.T) { // Set up UI auth that would normally block requests testHash, _ := bcrypt.GenerateFromPassword([]byte("testpass"), bcrypt.DefaultCost) - auth.SetUIAuth("testuser:" + string(testHash)) + if err := auth.SetUIAuth("testuser:" + string(testHash)); err != nil { + t.Fatalf("Failed to set UI auth: %s", err.Error()) + } r := apiRoutes() ts := httptest.NewServer(r) @@ -374,11 +376,15 @@ func TestSendAPIAuthMiddleware(t *testing.T) { // Set up UI auth uiHash, _ := bcrypt.GenerateFromPassword([]byte("uipass"), bcrypt.DefaultCost) - auth.SetUIAuth("uiuser:" + string(uiHash)) + if err := auth.SetUIAuth("uiuser:" + string(uiHash)); err != nil { + t.Fatalf("Failed to set UI auth: %s", err.Error()) + } // Set up dedicated Send API auth sendHash, _ := bcrypt.GenerateFromPassword([]byte("sendpass"), bcrypt.DefaultCost) - auth.SetSendAPIAuth("senduser:" + string(sendHash)) + if err := auth.SetSendAPIAuth("senduser:" + string(sendHash)); err != nil { + t.Fatalf("Failed to set Send API auth: %s", err.Error()) + } r := apiRoutes() ts := httptest.NewServer(r) @@ -421,7 +427,9 @@ func TestSendAPIAuthMiddleware(t *testing.T) { // Set up only UI auth uiHash, _ := bcrypt.GenerateFromPassword([]byte("uipass"), bcrypt.DefaultCost) - auth.SetUIAuth("uiuser:" + string(uiHash)) + if err := auth.SetUIAuth("uiuser:" + string(uiHash)); err != nil { + t.Fatalf("Failed to set UI auth: %s", err.Error()) + } r := apiRoutes() ts := httptest.NewServer(r) @@ -455,9 +463,14 @@ func TestSendAPIAuthMiddleware(t *testing.T) { // Set up UI auth and Send API auth uiHash, _ := bcrypt.GenerateFromPassword([]byte("uipass"), bcrypt.DefaultCost) - auth.SetUIAuth("uiuser:" + string(uiHash)) + if err := auth.SetUIAuth("uiuser:" + string(uiHash)); err != nil { + t.Fatalf("Failed to set UI auth: %s", err.Error()) + } + sendHash, _ := bcrypt.GenerateFromPassword([]byte("sendpass"), bcrypt.DefaultCost) - auth.SetSendAPIAuth("senduser:" + string(sendHash)) + if err := auth.SetSendAPIAuth("senduser:" + string(sendHash)); err != nil { + t.Fatalf("Failed to set Send API auth: %s", err.Error()) + } r := apiRoutes() ts := httptest.NewServer(r) @@ -604,7 +617,7 @@ func clientGet(url string) ([]byte, error) { return nil, fmt.Errorf("%s returned status %d", url, resp.StatusCode) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() data, err := io.ReadAll(resp.Body) @@ -625,7 +638,7 @@ func clientDelete(url, body string) ([]byte, error) { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("%s returned status %d", url, resp.StatusCode) @@ -650,7 +663,7 @@ func clientPut(url, body string) ([]byte, error) { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("%s returned status %d", url, resp.StatusCode) @@ -675,7 +688,7 @@ func clientPost(url, body string) ([]byte, error) { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("%s returned status %d", url, resp.StatusCode) @@ -702,7 +715,7 @@ func clientPostWithAuth(url, body, username, password string) ([]byte, error) { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("%s returned status %d", url, resp.StatusCode) @@ -728,7 +741,7 @@ func clientGetWithAuth(url, username, password string) ([]byte, error) { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("%s returned status %d", url, resp.StatusCode) diff --git a/server/webhook/webhook.go b/server/webhook/webhook.go index de16fb2..0b60d76 100644 --- a/server/webhook/webhook.go +++ b/server/webhook/webhook.go @@ -22,7 +22,7 @@ var ( ) // Send will post the MessageSummary to a webhook (if configured) -func Send(msg interface{}) { +func Send(msg any) { if config.WebhookURL == "" { return } @@ -70,7 +70,7 @@ func Send(msg interface{}) { return } - defer resp.Body.Close() + _ = resp.Body.Close() }) }() }