Skip to content

Commit

Permalink
Merge pull request #155 from fewlinesco/CU-9ux4p3_base64-encode-heade…
Browse files Browse the repository at this point in the history
…rs-only-when-necessary

This commit updates the function rfc822_encode to make it Base64 encode only when the content contains non-ASCII characters. Some spam filters have a policy, regarding encoded headers, that can lead to mark legit emails as spam. We should encode headers only when necessary.
  • Loading branch information
arnaudmorisset authored Nov 10, 2020
2 parents b8e0dec + dab73c3 commit c301cbd
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 7 deletions.
12 changes: 11 additions & 1 deletion lib/bamboo/adapters/smtp_adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,17 @@ defmodule Bamboo.SMTPAdapter do
end

defp rfc822_encode(content) do
"=?UTF-8?B?#{Base.encode64(content)}?="
if contains_only_ascii_characters?(content) do
"=?UTF-8?B?#{content}?="
else
"=?UTF-8?B?#{Base.encode64(content)}?="
end
end

defp contains_only_ascii_characters?(content) do
content
|> String.to_charlist()
|> List.ascii_printable?()
end

def base64_and_split(data) do
Expand Down
77 changes: 71 additions & 6 deletions test/lib/bamboo/adapters/smtp_adapter_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ defmodule Bamboo.SMTPAdapterTest do
assert format_email_as_string(bamboo_email.from, false) == from
assert format_email(bamboo_email.to ++ bamboo_email.cc ++ bamboo_email.bcc, false) == to

rfc822_subject = "Subject: =?UTF-8?B?SGVsbG8gZnJvbSBCYW1ib28=?=\r\n"
rfc822_subject = "Subject: =?UTF-8?B?Hello from Bamboo?=\r\n"
assert String.contains?(raw_email, rfc822_subject)

assert String.contains?(raw_email, "From: #{format_email_as_string(bamboo_email.from)}\r\n")
Expand Down Expand Up @@ -499,7 +499,7 @@ defmodule Bamboo.SMTPAdapterTest do
assert format_email_as_string(bamboo_email.from, false) == from
assert format_email(bamboo_email.to ++ bamboo_email.cc ++ bamboo_email.bcc, false) == to

rfc822_subject = "Subject: =?UTF-8?B?SGVsbG8gZnJvbSBCYW1ib28=?=\r\n"
rfc822_subject = "Subject: =?UTF-8?B?Hello from Bamboo?=\r\n"
assert String.contains?(raw_email, rfc822_subject)

assert String.contains?(raw_email, "From: #{format_email_as_string(bamboo_email.from)}\r\n")
Expand Down Expand Up @@ -547,7 +547,7 @@ defmodule Bamboo.SMTPAdapterTest do
assert format_email_as_string(bamboo_email.from, false) == from
assert format_email(bamboo_email.to ++ bamboo_email.cc ++ bamboo_email.bcc, false) == to

rfc822_subject = "Subject: =?UTF-8?B?SGVsbG8gZnJvbSBCYW1ib28=?=\r\n"
rfc822_subject = "Subject: =?UTF-8?B?Hello from Bamboo?=\r\n"
assert String.contains?(raw_email, rfc822_subject)

assert String.contains?(raw_email, "From: #{format_email_as_string(bamboo_email.from)}\r\n")
Expand Down Expand Up @@ -597,7 +597,7 @@ defmodule Bamboo.SMTPAdapterTest do
assert format_email_as_string(bamboo_email.from, false) == from
assert format_email(bamboo_email.to ++ bamboo_email.cc ++ bamboo_email.bcc, false) == to

rfc822_subject = "Subject: =?UTF-8?B?SGVsbG8gZnJvbSBCYW1ib28=?=\r\n"
rfc822_subject = "Subject: =?UTF-8?B?Hello from Bamboo?=\r\n"
assert String.contains?(raw_email, rfc822_subject)

assert String.contains?(raw_email, "From: #{format_email_as_string(bamboo_email.from)}\r\n")
Expand Down Expand Up @@ -646,7 +646,7 @@ defmodule Bamboo.SMTPAdapterTest do
assert format_email_as_string(bamboo_email.from, false) == from
assert format_email(bamboo_email.to ++ bamboo_email.cc ++ bamboo_email.bcc, false) == to

rfc822_subject = "Subject: =?UTF-8?B?SGVsbG8gZnJvbSBCYW1ib28=?=\r\n"
rfc822_subject = "Subject: =?UTF-8?B?Hello from Bamboo?=\r\n"
assert String.contains?(raw_email, rfc822_subject)

assert String.contains?(raw_email, "From: #{format_email_as_string(bamboo_email.from)}\r\n")
Expand Down Expand Up @@ -675,6 +675,61 @@ defmodule Bamboo.SMTPAdapterTest do
assert_configuration(bamboo_config, gen_smtp_config)
end

test "email looks fine when they have non-ASCII characters in subject, from and to" do
bamboo_email =
new_email(
from: {"Awesome Person 😎", "[email protected]"},
to: {"Person Awesome 🤩", "[email protected]"},
subject: "Hello! 👋"
)

bamboo_config = configuration()

{:ok, "200 Ok 1234567890"} = SMTPAdapter.deliver(bamboo_email, bamboo_config)

assert 1 = length(FakeGenSMTP.fetch_sent_emails())

[{{from, to, raw_email}, gen_smtp_config}] = FakeGenSMTP.fetch_sent_emails()

[multipart_header] =
Regex.run(
~r{Content-Type: multipart/alternative; boundary="([^"]+)"\r\n},
raw_email,
capture: :all_but_first
)

assert format_email_as_string(bamboo_email.from, false) == from
assert format_email(bamboo_email.to ++ bamboo_email.cc ++ bamboo_email.bcc, false) == to

rfc822_subject = "Subject: =?UTF-8?B?SGVsbG8hIPCfkYs=?=\r\n"
assert String.contains?(raw_email, rfc822_subject)

assert String.contains?(raw_email, "From: #{format_email_as_string(bamboo_email.from)}\r\n")
assert String.contains?(raw_email, "To: #{format_email_as_string(bamboo_email.to)}\r\n")
assert String.contains?(raw_email, "Cc: #{format_email_as_string(bamboo_email.cc)}\r\n")
assert String.contains?(raw_email, "Bcc: #{format_email_as_string(bamboo_email.bcc)}\r\n")
assert String.contains?(raw_email, "Reply-To: [email protected]\r\n")
assert String.contains?(raw_email, "MIME-Version: 1.0\r\n")

assert String.contains?(
raw_email,
"--#{multipart_header}\r\n" <>
"Content-Type: text/html;charset=UTF-8\r\n" <>
"Content-Transfer-Encoding: base64\r\n" <>
"\r\n" <>
"#{SMTPAdapter.base64_and_split(bamboo_email.html_body)}\r\n"
)

assert String.contains?(
raw_email,
"--#{multipart_header}\r\n" <>
"Content-Type: text/plain;charset=UTF-8\r\n" <>
"\r\n"
)

assert_configuration(bamboo_config, gen_smtp_config)
end

test "check rfc822 encoding for subject" do
bamboo_email =
@email_in_utf8
Expand Down Expand Up @@ -709,7 +764,17 @@ defmodule Bamboo.SMTPAdapterTest do
end

defp rfc822_encode(content) do
"=?UTF-8?B?#{Base.encode64(content)}?="
if contains_only_ascii_characters?(content) do
"=?UTF-8?B?#{content}?="
else
"=?UTF-8?B?#{Base.encode64(content)}?="
end
end

defp contains_only_ascii_characters?(content) do
content
|> String.to_charlist()
|> List.ascii_printable?()
end

defp assert_configuration(bamboo_config, gen_smtp_config) do
Expand Down

0 comments on commit c301cbd

Please sign in to comment.