summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2023-07-26 22:33:24 +0700
committerShulhan <ms@kilabit.info>2023-07-26 23:02:20 +0700
commitfbfe100dd48019b8012e02383e42d110a9e605ba (patch)
tree5b69d98f57621bdf0cc8dd52cc17cc5ea802d2d1
parentf156ef6d382427ec5eccd631070b510965452537 (diff)
downloadpakakeh.go-fbfe100dd48019b8012e02383e42d110a9e605ba.tar.xz
lib/smtp: format the passed data in NewMailTx
The following rules are applied to the data, * all lines must end with CRLF * if the line start with period, additional period is inserted before the line. This recommendation based on RFC 5321 section 4.5.2 to prevent data that contains CRLF "." CRLF does not corrupt the message, causing the server terminate reading the message where it should not. [1] https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.2
-rw-r--r--lib/smtp/mail_tx.go43
-rw-r--r--lib/smtp/mail_tx_test.go10
2 files changed, 46 insertions, 7 deletions
diff --git a/lib/smtp/mail_tx.go b/lib/smtp/mail_tx.go
index edb2c0f2..620e7b35 100644
--- a/lib/smtp/mail_tx.go
+++ b/lib/smtp/mail_tx.go
@@ -16,23 +16,23 @@ type MailTx struct {
Postpone time.Time
// Received contains the time when the message arrived on server.
- // This field is ignored in Client.Send().
+ // This field is ignored in Client.MailTx.
Received time.Time
// ID of message.
- // This field is ignored in Client.Send().
+ // This field is ignored in Client.MailTx.
ID string
// From contains originator address.
- // This field is required in Client.Send().
+ // This field is required in Client.MailTx.
From string
// Recipients contains list of the destination address.
- // This field is required in Client.Send().
+ // This field is required in Client.MailTx.
Recipients []string
// Data contains content of message.
- // This field is optional in Client.Send().
+ // This field is optional in Client.MailTx.
Data []byte
Retry int
@@ -47,8 +47,7 @@ func NewMailTx(from string, to []string, data []byte) (mail *MailTx) {
}
mail.ID = strconv.FormatInt(mail.Received.UnixNano(), 10)
- mail.Data = make([]byte, len(data))
- copy(mail.Data, data)
+ mail.Data = format(data)
return
}
@@ -87,3 +86,33 @@ func (mail *MailTx) seal(clientDomain, clientAddress, localAddress string) {
mail.Received.Format(time.RFC1123Z))
mail.Data = append([]byte(line), mail.Data...)
}
+
+// format format the email data by ending all line with CRLF and adding
+// period to line that start with period.
+func format(in []byte) (out []byte) {
+ var (
+ isNewLine = true
+
+ prevc byte
+ x int
+ )
+
+ out = make([]byte, 0, len(in))
+ for x < len(in) {
+ if isNewLine {
+ if in[x] == '.' {
+ out = append(out, '.')
+ }
+ isNewLine = false
+ } else if in[x] == '\n' {
+ if prevc != '\r' {
+ out = append(out, '\r')
+ }
+ isNewLine = true
+ }
+ out = append(out, in[x])
+ prevc = in[x]
+ x++
+ }
+ return out
+}
diff --git a/lib/smtp/mail_tx_test.go b/lib/smtp/mail_tx_test.go
index 9e8fe8c6..ee78b052 100644
--- a/lib/smtp/mail_tx_test.go
+++ b/lib/smtp/mail_tx_test.go
@@ -40,3 +40,13 @@ func TestMailTx_isTerminated(t *testing.T) {
test.Assert(t, "isTerminated", c.exp, c.mail.isTerminated())
}
}
+
+func TestFormat(t *testing.T) {
+ var (
+ in = ".\n..\r\na.text.\n.message\r\n.\r\n"
+ exp = "..\r\n...\r\na.text.\r\n..message\r\n..\r\n"
+ got []byte
+ )
+ got = format([]byte(in))
+ test.Assert(t, `format`, []byte(exp), got)
+}