mirror of
https://github.com/mjl-/mox.git
synced 2024-12-26 16:33:47 +03:00
361bc2b516
because that is what most of the code expects. we could work around having bare lf, but it would complicate too much code. currently, a message with bare lf is accepted (in smtpserver delivery, imapserver append, etc), but when an imap session would try to fetch parsed parts, that would fail because and even cause a imapserver panic (closing the connection). in message imports we would already convert bare lf to crlf (because it is expected those messages are all lf-only-ending). we store messages with crlf-ending instead of lf-ending so the imapserver has all correct information at hand (line counts, byte counts). found by using emclient with mox. it adds a message to the inbox that can have mixed crlf and bare lf line endings in a few header fields (in some localization, emclient authors explained how that happened, thanks!). we can now convert those lines and read those messages over imap. emclient already switched to all-crlf line endings in newer (development) versions.
100 lines
2 KiB
Go
100 lines
2 KiB
Go
package message
|
|
|
|
import (
|
|
"io"
|
|
)
|
|
|
|
// Writer is a write-through helper, collecting properties about the written
|
|
// message and replacing bare \n line endings with \r\n.
|
|
type Writer struct {
|
|
writer io.Writer
|
|
|
|
HaveBody bool // Body is optional. ../rfc/5322:343
|
|
Has8bit bool // Whether a byte with the high/8bit has been read. So whether this is 8BITMIME instead of 7BIT.
|
|
Size int64
|
|
|
|
tail [3]byte // For detecting header/body-separating crlf.
|
|
// todo: should be parsing headers here, as we go
|
|
}
|
|
|
|
func NewWriter(w io.Writer) *Writer {
|
|
// Pretend we already saw \r\n, for handling empty header.
|
|
return &Writer{writer: w, tail: [3]byte{0, '\r', '\n'}}
|
|
}
|
|
|
|
// Write implements io.Writer.
|
|
func (w *Writer) Write(buf []byte) (int, error) {
|
|
origtail := w.tail
|
|
|
|
if !w.HaveBody && len(buf) > 0 {
|
|
get := func(i int) byte {
|
|
if i < 0 {
|
|
return w.tail[3+i]
|
|
}
|
|
return buf[i]
|
|
}
|
|
|
|
for i, b := range buf {
|
|
if b == '\n' && (get(i-1) == '\n' || get(i-1) == '\r' && get(i-2) == '\n') {
|
|
w.HaveBody = true
|
|
break
|
|
}
|
|
}
|
|
|
|
n := len(buf)
|
|
if n > 3 {
|
|
n = 3
|
|
}
|
|
copy(w.tail[:], w.tail[n:])
|
|
copy(w.tail[3-n:], buf[len(buf)-n:])
|
|
}
|
|
if !w.Has8bit {
|
|
for _, b := range buf {
|
|
if b&0x80 != 0 {
|
|
w.Has8bit = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
wrote := 0
|
|
o := 0
|
|
Top:
|
|
for o < len(buf) {
|
|
for i := o; i < len(buf); i++ {
|
|
if buf[i] == '\n' && (i > 0 && buf[i-1] != '\r' || i == 0 && origtail[2] != '\r') {
|
|
// Write buffer leading up to missing \r.
|
|
if i > o+1 {
|
|
n, err := w.writer.Write(buf[o:i])
|
|
if n > 0 {
|
|
wrote += n
|
|
w.Size += int64(n)
|
|
}
|
|
if err != nil {
|
|
return wrote, err
|
|
}
|
|
}
|
|
n, err := w.writer.Write([]byte{'\r', '\n'})
|
|
if n == 2 {
|
|
wrote += 1 // For only the newline.
|
|
w.Size += int64(2)
|
|
}
|
|
if err != nil {
|
|
return wrote, err
|
|
}
|
|
o = i + 1
|
|
continue Top
|
|
}
|
|
}
|
|
n, err := w.writer.Write(buf[o:])
|
|
if n > 0 {
|
|
wrote += n
|
|
w.Size += int64(n)
|
|
}
|
|
if err != nil {
|
|
return wrote, err
|
|
}
|
|
break
|
|
}
|
|
return wrote, nil
|
|
}
|