rename variables, struct fields and functions to include an "x" when they can panic for handling errors

and document the convention in develop.txt.
spurred by running errcheck again (it has been a while). it still has too many
false to enable by default.
This commit is contained in:
Mechiel Lukkien
2025-03-24 16:02:12 +01:00
parent a2c79e25c1
commit 7a87522be0
18 changed files with 797 additions and 800 deletions

View File

@ -428,9 +428,9 @@ func (cmd *fetchCmd) process(atts []fetchAtt) {
}
// Write errors are turned into panics because we write through c.
fmt.Fprintf(cmd.conn.bw, "* %d FETCH ", cmd.conn.xsequence(cmd.uid))
data.writeTo(cmd.conn, cmd.conn.bw)
cmd.conn.bw.Write([]byte("\r\n"))
fmt.Fprintf(cmd.conn.xbw, "* %d FETCH ", cmd.conn.xsequence(cmd.uid))
data.writeTo(cmd.conn, cmd.conn.xbw)
cmd.conn.xbw.Write([]byte("\r\n"))
}
// result for one attribute. if processing fails, e.g. because data was requested

View File

@ -264,7 +264,7 @@ func (c *conn) cmdList(tag, cmd string, p *parser) {
c.bwritelinef("%s", line)
}
for _, meta := range respMetadata {
meta.writeTo(c, c.bw)
meta.writeTo(c, c.xbw)
c.bwritelinef("")
}
c.ok(tag, cmd)

View File

@ -155,18 +155,18 @@ func (c *conn) cmdGetmetadata(tag, cmd string, p *parser) {
// Response syntax: ../rfc/5464:807 ../rfc/5464:778
// We can only send untagged responses when we have any matches.
if len(annotations) > 0 {
fmt.Fprintf(c.bw, "* METADATA %s (", mailboxt(mailboxName).pack(c))
fmt.Fprintf(c.xbw, "* METADATA %s (", mailboxt(mailboxName).pack(c))
for i, a := range annotations {
if i > 0 {
fmt.Fprint(c.bw, " ")
fmt.Fprint(c.xbw, " ")
}
astring(a.Key).writeTo(c, c.bw)
fmt.Fprint(c.bw, " ")
astring(a.Key).writeTo(c, c.xbw)
fmt.Fprint(c.xbw, " ")
if a.IsString {
string0(string(a.Value)).writeTo(c, c.bw)
string0(string(a.Value)).writeTo(c, c.xbw)
} else {
v := readerSizeSyncliteral{bytes.NewReader(a.Value), int64(len(a.Value)), true}
v.writeTo(c, c.bw)
v.writeTo(c, c.xbw)
}
}
c.bwritelinef(")")

View File

@ -9,7 +9,7 @@ import (
type token interface {
pack(c *conn) string
writeTo(c *conn, w io.Writer)
writeTo(c *conn, xw io.Writer) // Writes to xw panic on error.
}
type bare string
@ -18,8 +18,8 @@ func (t bare) pack(c *conn) string {
return string(t)
}
func (t bare) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
func (t bare) writeTo(c *conn, xw io.Writer) {
xw.Write([]byte(t.pack(c)))
}
type niltoken struct{}
@ -30,8 +30,8 @@ func (t niltoken) pack(c *conn) string {
return "NIL"
}
func (t niltoken) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
func (t niltoken) writeTo(c *conn, xw io.Writer) {
xw.Write([]byte(t.pack(c)))
}
func nilOrString(s string) token {
@ -60,8 +60,8 @@ func (t string0) pack(c *conn) string {
return r
}
func (t string0) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
func (t string0) writeTo(c *conn, xw io.Writer) {
xw.Write([]byte(t.pack(c)))
}
type dquote string
@ -78,8 +78,8 @@ func (t dquote) pack(c *conn) string {
return r
}
func (t dquote) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
func (t dquote) writeTo(c *conn, xw io.Writer) {
xw.Write([]byte(t.pack(c)))
}
type syncliteral string
@ -88,9 +88,9 @@ func (t syncliteral) pack(c *conn) string {
return fmt.Sprintf("{%d}\r\n", len(t)) + string(t)
}
func (t syncliteral) writeTo(c *conn, w io.Writer) {
fmt.Fprintf(w, "{%d}\r\n", len(t))
w.Write([]byte(t))
func (t syncliteral) writeTo(c *conn, xw io.Writer) {
fmt.Fprintf(xw, "{%d}\r\n", len(t))
xw.Write([]byte(t))
}
// data from reader with known size.
@ -112,14 +112,14 @@ func (t readerSizeSyncliteral) pack(c *conn) string {
return fmt.Sprintf("%s{%d}\r\n", lit, t.size) + string(buf)
}
func (t readerSizeSyncliteral) writeTo(c *conn, w io.Writer) {
func (t readerSizeSyncliteral) writeTo(c *conn, xw io.Writer) {
var lit string
if t.lit8 {
lit = "~"
}
fmt.Fprintf(w, "%s{%d}\r\n", lit, t.size)
fmt.Fprintf(xw, "%s{%d}\r\n", lit, t.size)
defer c.xtrace(mlog.LevelTracedata)()
if _, err := io.Copy(w, io.LimitReader(t.r, t.size)); err != nil {
if _, err := io.Copy(xw, io.LimitReader(t.r, t.size)); err != nil {
panic(err)
}
}
@ -137,17 +137,14 @@ func (t readerSyncliteral) pack(c *conn) string {
return fmt.Sprintf("{%d}\r\n", len(buf)) + string(buf)
}
func (t readerSyncliteral) writeTo(c *conn, w io.Writer) {
func (t readerSyncliteral) writeTo(c *conn, xw io.Writer) {
buf, err := io.ReadAll(t.r)
if err != nil {
panic(err)
}
fmt.Fprintf(w, "{%d}\r\n", len(buf))
fmt.Fprintf(xw, "{%d}\r\n", len(buf))
defer c.xtrace(mlog.LevelTracedata)()
_, err = w.Write(buf)
if err != nil {
panic(err)
}
xw.Write(buf)
}
// list with tokens space-separated
@ -165,15 +162,15 @@ func (t listspace) pack(c *conn) string {
return s
}
func (t listspace) writeTo(c *conn, w io.Writer) {
fmt.Fprint(w, "(")
func (t listspace) writeTo(c *conn, xw io.Writer) {
fmt.Fprint(xw, "(")
for i, e := range t {
if i > 0 {
fmt.Fprint(w, " ")
fmt.Fprint(xw, " ")
}
e.writeTo(c, w)
e.writeTo(c, xw)
}
fmt.Fprint(w, ")")
fmt.Fprint(xw, ")")
}
// concatenate tokens space-separated
@ -190,12 +187,12 @@ func (t concatspace) pack(c *conn) string {
return s
}
func (t concatspace) writeTo(c *conn, w io.Writer) {
func (t concatspace) writeTo(c *conn, xw io.Writer) {
for i, e := range t {
if i > 0 {
fmt.Fprint(w, " ")
fmt.Fprint(xw, " ")
}
e.writeTo(c, w)
e.writeTo(c, xw)
}
}
@ -210,9 +207,9 @@ func (t concat) pack(c *conn) string {
return s
}
func (t concat) writeTo(c *conn, w io.Writer) {
func (t concat) writeTo(c *conn, xw io.Writer) {
for _, e := range t {
e.writeTo(c, w)
e.writeTo(c, xw)
}
}
@ -234,8 +231,8 @@ next:
return string(t)
}
func (t astring) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
func (t astring) writeTo(c *conn, xw io.Writer) {
xw.Write([]byte(t.pack(c)))
}
// mailbox with utf7 encoding if connection requires it, or utf8 otherwise.
@ -249,8 +246,8 @@ func (t mailboxt) pack(c *conn) string {
return astring(s).pack(c)
}
func (t mailboxt) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
func (t mailboxt) writeTo(c *conn, xw io.Writer) {
xw.Write([]byte(t.pack(c)))
}
type number uint32
@ -259,6 +256,6 @@ func (t number) pack(c *conn) string {
return fmt.Sprintf("%d", t)
}
func (t number) writeTo(c *conn, w io.Writer) {
w.Write([]byte(t.pack(c)))
func (t number) writeTo(c *conn, xw io.Writer) {
xw.Write([]byte(t.pack(c)))
}

View File

@ -191,14 +191,16 @@ type conn struct {
tls bool // Whether TLS has been initialized.
viaHTTPS bool // Whether this connection came in via HTTPS (using TLS ALPN).
br *bufio.Reader // From remote, with TLS unwrapped in case of TLS, and possibly wrapping inflate.
tr *moxio.TraceReader // Kept to change trace level when reading/writing cmd/auth/data.
line chan lineErr // If set, instead of reading from br, a line is read from this channel. For reading a line in IDLE while also waiting for mailbox/account updates.
lastLine string // For detecting if syntax error is fatal, i.e. if this ends with a literal. Without crlf.
bw *bufio.Writer // To remote, with TLS added in case of TLS, and possibly wrapping deflate, see conn.flateWriter.
tr *moxio.TraceReader // Kept to change trace level when reading/writing cmd/auth/data.
tw *moxio.TraceWriter
slow bool // If set, reads are done with a 1 second sleep, and writes are done 1 byte at a time, to keep spammers busy.
lastlog time.Time // For printing time since previous log line.
baseTLSConfig *tls.Config // Base TLS config to use for handshake.
xbw *bufio.Writer // To remote, with TLS added in case of TLS, and possibly wrapping deflate, see conn.xflateWriter. Writes go through xtw to conn.Write, which panics on errors, hence the "x".
xtw *moxio.TraceWriter
xflateWriter *moxio.FlateWriter // For flushing output after flushing conn.xbw, and for closing.
xflateBW *bufio.Writer // Wraps raw connection writes, xflateWriter writes here, also needs flushing.
slow bool // If set, reads are done with a 1 second sleep, and writes are done 1 byte at a time, to keep spammers busy.
lastlog time.Time // For printing time since previous log line.
baseTLSConfig *tls.Config // Base TLS config to use for handshake.
remoteIP net.IP
noRequireSTARTTLS bool
cmd string // Currently executing, for deciding to applyChanges and logging.
@ -208,8 +210,6 @@ type conn struct {
log mlog.Log // Used for all synchronous logging on this connection, see logbg for logging in a separate goroutine.
enabled map[capability]bool // All upper-case.
compress bool // Whether compression is enabled, via compress command.
flateWriter *moxio.FlateWriter // For flushing output after flushing conn.bw, and for closing.
flateBW *bufio.Writer // Wraps raw connection writes, flateWriter writes here, also needs flushing.
// Set by SEARCH with SAVE. Can be used by commands accepting a sequence-set with
// value "$". When used, UIDs must be verified to still exist, because they may
@ -529,11 +529,11 @@ func (c *conn) Write(buf []byte) (int, error) {
func (c *conn) xtrace(level slog.Level) func() {
c.xflush()
c.tr.SetTrace(level)
c.tw.SetTrace(level)
c.xtw.SetTrace(level)
return func() {
c.xflush()
c.tr.SetTrace(mlog.LevelTrace)
c.tw.SetTrace(mlog.LevelTrace)
c.xtw.SetTrace(mlog.LevelTrace)
}
}
@ -641,7 +641,7 @@ func (c *conn) writelinef(format string, args ...any) {
// Buffer line for write.
func (c *conn) bwritelinef(format string, args ...any) {
format += "\r\n"
fmt.Fprintf(c.bw, format, args...)
fmt.Fprintf(c.xbw, format, args...)
}
func (c *conn) xflush() {
@ -650,7 +650,7 @@ func (c *conn) xflush() {
return
}
err := c.bw.Flush()
err := c.xbw.Flush()
xcheckf(err, "flush") // Should never happen, the Write caused by the Flush should panic on i/o error.
// If compression is enabled, we need to flush its stream.
@ -658,11 +658,11 @@ func (c *conn) xflush() {
// Note: Flush writes a sync message if there is nothing to flush. Ideally we
// wouldn't send that, but we would have to keep track of whether data needs to be
// flushed.
err := c.flateWriter.Flush()
err := c.xflateWriter.Flush()
xcheckf(err, "flush deflate")
// The flate writer writes to a bufio.Writer, we must also flush that.
err = c.flateBW.Flush()
err = c.xflateBW.Flush()
xcheckf(err, "flush deflate writer")
}
}
@ -753,8 +753,8 @@ func serve(listenerName string, cid int64, tlsConfig *tls.Config, nc net.Conn, x
c.tr = moxio.NewTraceReader(c.log, "C: ", c.conn)
// todo: tracing should be done on whatever comes out of c.br. the remote connection write a command plus data, and bufio can read it in one read, causing a command parser that sets the tracing level to data to have no effect. we are now typically logging sent messages, when mail clients append to the Sent mailbox.
c.br = bufio.NewReader(c.tr)
c.tw = moxio.NewTraceWriter(c.log, "S: ", c)
c.bw = bufio.NewWriter(c.tw)
c.xtw = moxio.NewTraceWriter(c.log, "S: ", c)
c.xbw = bufio.NewWriter(c.xtw)
// Many IMAP connections use IDLE to wait for new incoming messages. We'll enable
// keepalive to get a higher chance of the connection staying alive, or otherwise
@ -1144,9 +1144,9 @@ func (c *conn) command() {
// If compression was enabled, we flush & close the deflate stream.
if c.compress {
// Note: Close and flush can Write and may panic with an i/o error.
if err := c.flateWriter.Close(); err != nil {
if err := c.xflateWriter.Close(); err != nil {
c.log.Debugx("close deflate writer", err)
} else if err := c.flateBW.Flush(); err != nil {
} else if err := c.xflateBW.Flush(); err != nil {
c.log.Debugx("flush deflate buffer", err)
}
}
@ -1870,15 +1870,15 @@ func (c *conn) cmdCompress(tag, cmd string, p *parser) {
c.log.Debug("compression enabled")
c.ok(tag, cmd)
c.flateBW = bufio.NewWriter(c)
fw0, err := flate.NewWriter(c.flateBW, flate.DefaultCompression)
c.xflateBW = bufio.NewWriter(c)
fw0, err := flate.NewWriter(c.xflateBW, flate.DefaultCompression)
xcheckf(err, "deflate") // Cannot happen.
fw := moxio.NewFlateWriter(fw0)
xfw := moxio.NewFlateWriter(fw0)
c.compress = true
c.flateWriter = fw
c.tw = moxio.NewTraceWriter(c.log, "S: ", c.flateWriter)
c.bw = bufio.NewWriter(c.tw) // The previous c.bw will not have buffered data.
c.xflateWriter = xfw
c.xtw = moxio.NewTraceWriter(c.log, "S: ", c.xflateWriter)
c.xbw = bufio.NewWriter(c.xtw) // The previous c.xbw will not have buffered data.
rc := xprefixConn(c.conn, c.br) // c.br may contain buffered data.
// We use the special partial reader. Some clients write commands and flush the