Browse Source

Add quoted-printable decoding to smtp server

Some e-mails are sent using quoted-printable encoding [0], resulting in
notifications with weird characters.

This commit adds support for this encoding, resulting in the following:

**Before**
```
A
=3D=3D=3D=3D=3D
B
=3D=3D=3D=3D=3D
C
```

**After**
```
A
=====
B
=====
C
```

[0] https://www.rfc-editor.org/rfc/rfc2045.html
Guillaume Taquet Gasperini 2 năm trước cách đây
mục cha
commit
5b8520b4e0
2 tập tin đã thay đổi với 80 bổ sung0 xóa
  1. 3 0
      server/smtp_server.go
  2. 77 0
      server/smtp_server_test.go

+ 3 - 0
server/smtp_server.go

@@ -9,6 +9,7 @@ import (
 	"io"
 	"mime"
 	"mime/multipart"
+	"mime/quotedprintable"
 	"net"
 	"net/http"
 	"net/http/httptest"
@@ -265,6 +266,8 @@ func readMultipartMailBody(body io.Reader, params map[string]string, depth int)
 func readPlainTextMailBody(reader io.Reader, transferEncoding string) (string, error) {
 	if strings.ToLower(transferEncoding) == "base64" {
 		reader = base64.NewDecoder(base64.StdEncoding, reader)
+	} else if strings.ToLower(transferEncoding) == "quoted-printable" {
+		reader = quotedprintable.NewReader(reader)
 	}
 	body, err := io.ReadAll(reader)
 	if err != nil {

+ 77 - 0
server/smtp_server_test.go

@@ -303,6 +303,39 @@ BBBBBBBBBBBBBBBBBBBBBBBBB`
 	writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
 }
 
+func TestSmtpBackend_Plaintext_QuotedPrintable(t *testing.T) {
+	email := `EHLO example.com
+MAIL FROM: phil@example.com
+RCPT TO: mytopic@ntfy.sh
+DATA
+Date: Tue, 28 Dec 2021 00:30:10 +0100
+Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
+Subject: and one more
+From: Phil <phil@example.com>
+To: mytopic@ntfy.sh
+Content-Type: text/plain; charset="UTF-8"
+Content-Transfer-Encoding: quoted-printable
+
+what's
+=C3=A0&=C3=A9"'(-=C3=A8_=C3=A7=C3=A0)
+=3D=3D=3D=3D=3D
+up
+.
+`
+	s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
+		require.Equal(t, "/mytopic", r.URL.Path)
+		require.Equal(t, "and one more", r.Header.Get("Title"))
+		require.Equal(t, `what's
+à&é"'(-è_çà)
+=====
+up`, readAll(t, r.Body))
+	})
+	conf.SMTPServerAddrPrefix = ""
+	defer s.Close()
+	defer c.Close()
+	writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
+}
+
 func TestSmtpBackend_Unsupported(t *testing.T) {
 	email := `EHLO example.com
 MAIL FROM: phil@example.com
@@ -390,6 +423,50 @@ L0VOIj4KClRoaXMgaXMgYSB0ZXN0IG1lc3NhZ2UgZnJvbSBUcnVlTkFTIENPUkUuCg==
 	writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
 }
 
+
+func TestSmtpBackend_MultipartQuotedPrintable(t *testing.T) {
+	email := `EHLO example.com
+MAIL FROM: phil@example.com
+RCPT TO: ntfy-mytopic@ntfy.sh
+DATA
+MIME-Version: 1.0
+Date: Tue, 28 Dec 2021 00:30:10 +0100
+Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
+Subject: and one more
+From: Phil <phil@example.com>
+To: ntfy-mytopic@ntfy.sh
+Content-Type: multipart/alternative; boundary="000000000000f3320b05d42915c9"
+
+--000000000000f3320b05d42915c9
+Content-Type: text/html; charset="UTF-8"
+
+html, ignore me
+
+--000000000000f3320b05d42915c9
+Content-Type: text/plain; charset="UTF-8"
+Content-Transfer-Encoding: quoted-printable
+
+what's
+=C3=A0&=C3=A9"'(-=C3=A8_=C3=A7=C3=A0)
+=3D=3D=3D=3D=3D
+up
+
+--000000000000f3320b05d42915c9--
+.
+`
+	s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
+		require.Equal(t, "/mytopic", r.URL.Path)
+		require.Equal(t, "and one more", r.Header.Get("Title"))
+		require.Equal(t,  `what's
+à&é"'(-è_çà)
+=====
+up`, readAll(t, r.Body))
+	})
+	defer s.Close()
+	defer c.Close()
+	writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
+}
+
 func TestSmtpBackend_NestedMultipartBase64(t *testing.T) {
 	email := `EHLO example.com
 MAIL FROM: test@mydomain.me