mox/moxio/base64writer.go
Mechiel Lukkien 69d2699961
write base64 message parts with 76 data bytes on a line instead of 78
As required by RFC 2045 (MIME). The 78 byte lines work in practice, except that
SpamAssassin has rules that give messages with 78-byte lines spam points.

Mentioned by kjetilho on irc.
2025-04-03 10:22:15 +02:00

72 lines
1.2 KiB
Go

package moxio
import (
"encoding/base64"
"io"
)
// implement io.Closer
type closerFunc func() error
func (f closerFunc) Close() error {
return f()
}
// Base64Writer turns a writer for data into one that writes base64 content on
// \r\n separated lines of max 76+2 characters length.
func Base64Writer(w io.Writer) io.WriteCloser {
lw := &lineWrapper{w: w}
bw := base64.NewEncoder(base64.StdEncoding, lw)
return struct {
io.Writer
io.Closer
}{
Writer: bw,
Closer: closerFunc(func() error {
if err := bw.Close(); err != nil {
return err
}
return lw.Close()
}),
}
}
type lineWrapper struct {
w io.Writer
n int // Written on current line.
}
func (lw *lineWrapper) Write(buf []byte) (int, error) {
wrote := 0
for len(buf) > 0 {
// base64 has max 76 data bytes on per line. ../rfc/2045:1372
n := min(76-lw.n, len(buf))
nn, err := lw.w.Write(buf[:n])
if nn > 0 {
wrote += nn
buf = buf[nn:]
}
if err != nil {
return wrote, err
}
lw.n += nn
if lw.n == 76 {
_, err := lw.w.Write([]byte("\r\n"))
if err != nil {
return wrote, err
}
lw.n = 0
}
}
return wrote, nil
}
func (lw *lineWrapper) Close() error {
if lw.n > 0 {
lw.n = 0
_, err := lw.w.Write([]byte("\r\n"))
return err
}
return nil
}