diff options
| author | Shulhan <ms@kilabit.info> | 2023-07-26 22:33:24 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2023-07-26 23:02:20 +0700 |
| commit | fbfe100dd48019b8012e02383e42d110a9e605ba (patch) | |
| tree | 5b69d98f57621bdf0cc8dd52cc17cc5ea802d2d1 | |
| parent | f156ef6d382427ec5eccd631070b510965452537 (diff) | |
| download | pakakeh.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.go | 43 | ||||
| -rw-r--r-- | lib/smtp/mail_tx_test.go | 10 |
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) +} |
