2023-01-30 16:27:06 +03:00
|
|
|
package message
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestMsgWriter(t *testing.T) {
|
|
|
|
check := func(data string, want bool) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
b := &strings.Builder{}
|
2023-08-11 15:07:49 +03:00
|
|
|
mw := NewWriter(b)
|
2023-01-30 16:27:06 +03:00
|
|
|
if _, err := mw.Write([]byte(data)); err != nil {
|
|
|
|
t.Fatalf("write for message %q: %s", data, err)
|
|
|
|
}
|
2023-08-11 15:07:49 +03:00
|
|
|
if mw.HaveBody != want {
|
|
|
|
t.Fatalf("got %v, expected %v, for message %q", mw.HaveBody, want, data)
|
2023-01-30 16:27:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
b = &strings.Builder{}
|
2023-08-11 15:07:49 +03:00
|
|
|
mw = NewWriter(b)
|
2023-01-30 16:27:06 +03:00
|
|
|
for i := range data {
|
|
|
|
if _, err := mw.Write([]byte(data[i : i+1])); err != nil {
|
|
|
|
t.Fatalf("write for message %q: %s", data, err)
|
|
|
|
}
|
|
|
|
}
|
2023-08-11 15:07:49 +03:00
|
|
|
if mw.HaveBody != want {
|
|
|
|
t.Fatalf("got %v, expected %v, for message %q", mw.HaveBody, want, data)
|
2023-01-30 16:27:06 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
check("no header", false)
|
|
|
|
check("no header\r\n", false)
|
|
|
|
check("key: value\r\n\r\n", true)
|
|
|
|
check("key: value\r\n\r\nbody", true)
|
when accepting an incoming message, turn any bare newlines (without carriage return) into crlf
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.
2023-11-21 15:19:54 +03:00
|
|
|
check("key: value\n\nbody", true)
|
|
|
|
check("key: value\n\r\nbody", true)
|
2023-01-30 16:27:06 +03:00
|
|
|
check("key: value\r\rbody", false)
|
|
|
|
check("\r\n\r\n", true)
|
|
|
|
check("\r\n\r\nbody", true)
|
2023-08-11 15:07:49 +03:00
|
|
|
check("\r\nbody", true)
|
when accepting an incoming message, turn any bare newlines (without carriage return) into crlf
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.
2023-11-21 15:19:54 +03:00
|
|
|
|
|
|
|
// Check \n is replaced with \r\n.
|
|
|
|
var b strings.Builder
|
|
|
|
mw := NewWriter(&b)
|
2024-01-01 20:30:31 +03:00
|
|
|
msg := "key: value\n\nline1\r\nline2\nx\n.\n"
|
when accepting an incoming message, turn any bare newlines (without carriage return) into crlf
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.
2023-11-21 15:19:54 +03:00
|
|
|
_, err := mw.Write([]byte(msg))
|
|
|
|
tcheck(t, err, "write")
|
|
|
|
got := b.String()
|
2024-01-01 20:30:31 +03:00
|
|
|
exp := "key: value\r\n\r\nline1\r\nline2\r\nx\r\n.\r\n"
|
when accepting an incoming message, turn any bare newlines (without carriage return) into crlf
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.
2023-11-21 15:19:54 +03:00
|
|
|
if got != exp {
|
|
|
|
t.Fatalf("got %q, expected %q", got, exp)
|
|
|
|
}
|
2023-01-30 16:27:06 +03:00
|
|
|
}
|
2024-02-08 14:33:19 +03:00
|
|
|
|
|
|
|
func TestMsgWriterIssue117(t *testing.T) {
|
|
|
|
var b strings.Builder
|
|
|
|
mw := NewWriter(&b)
|
|
|
|
|
|
|
|
// Write header and header/body separator, but with CR missing.
|
|
|
|
_, err := mw.Write([]byte("a: b\n\n"))
|
|
|
|
tcheck(t, err, "write")
|
|
|
|
|
|
|
|
// Write start of a line. The newline follows in a second write. Just because of buffering.
|
|
|
|
_, err = mw.Write([]byte("body\r"))
|
|
|
|
tcheck(t, err, "write")
|
|
|
|
|
|
|
|
// Finish the line. The bug is that w.tail was only updated while writing headers,
|
|
|
|
// not while writing message data for the body. That makes the code think this \n
|
|
|
|
// wasn't preceded by a \r, causing it to add a \r. And we end up with \r\r\n in
|
|
|
|
// the file.
|
|
|
|
_, err = mw.Write([]byte("\n"))
|
|
|
|
tcheck(t, err, "write")
|
|
|
|
|
|
|
|
got := b.String()
|
|
|
|
exp := "a: b\r\n\r\nbody\r\n"
|
|
|
|
if got != exp {
|
|
|
|
t.Fatalf("got %q, expected %q", got, exp)
|
|
|
|
}
|
|
|
|
}
|