diff --git a/bleach/linkifier.py b/bleach/linkifier.py
index 68a4042e..df56f3c3 100644
--- a/bleach/linkifier.py
+++ b/bleach/linkifier.py
@@ -1,5 +1,7 @@
import re
+from urllib.parse import quote
+
from bleach import callbacks as linkify_callbacks
from bleach import html5lib_shim
@@ -298,10 +300,15 @@ def handle_email_addresses(self, src_iter):
{"type": "Characters", "data": text[end : match.start()]}
)
+ # URL-encode the "local-part" according to RFC6068
+ parts = match.group(0).split("@")
+ parts[0] = quote(parts[0])
+ address = "@".join(parts)
+
# Run attributes through the callbacks to see what we
# should do with this match
attrs = {
- (None, "href"): "mailto:%s" % match.group(0),
+ (None, "href"): "mailto:%s" % address,
"_text": match.group(0),
}
attrs = self.apply_callbacks(attrs, True)
diff --git a/tests/test_linkify.py b/tests/test_linkify.py
index 69181ca2..fbabf122 100644
--- a/tests/test_linkify.py
+++ b/tests/test_linkify.py
@@ -104,6 +104,17 @@ def ft(attrs, new=False):
),
# Incorrect email
('"\\\n"@opa.ru', True, '"\\\n"@opa.ru'),
+ # RFC6068 special characters
+ (
+ "gorby%kremvax@example.com",
+ True,
+ 'gorby%kremvax@example.com',
+ ),
+ (
+ "unlikely?address@example.com",
+ True,
+ 'unlikely?address@example.com',
+ ),
],
)
def test_email_link(data, parse_email, expected):
@@ -115,15 +126,15 @@ def test_email_link(data, parse_email, expected):
[
(
'"james"@example.com',
- """"james"@example.com""",
+ """"james"@example.com""",
),
(
'"j\'ames"@example.com',
- """"j'ames"@example.com""",
+ """"j'ames"@example.com""",
),
(
'"ja>mes"@example.com',
- """"ja>mes"@example.com""",
+ """"ja>mes"@example.com""",
),
],
)