Skip to content

Commit 5c9973d

Browse files
committed
Normalize some mailbox names like postmaster to lowercase per RFC 2142
1 parent de6527f commit 5c9973d

File tree

5 files changed

+25
-2
lines changed

5 files changed

+25
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ There are no significant changes to which email addresses are considered valid/i
1212
* The quoted-string local part syntax (e.g. multiple @-signs, spaces, etc. if surrounded by quotes) and domain-literal addresses (e.g. @[192.XXX...] or @[IPv6:...]) are now parsed but not considered valid by default. Better error messages are now given for these addresses since it can be confusing for a technically valid address to be rejected, and new allow_quoted_local and allow_domain_literal options are added to allow these addresses if you really need them.
1313
* Some other error messages have changed to not repeat the email address in the error message.
1414
* The `email` field on the returned `ValidatedEmail` object has been renamed to `normalized` to be clearer about its importance, but access via `.email` is also still supported.
15+
* Some mailbox names like `postmaster` are now normalized to lowercase per RFC 2142.
1516
* The library has been reorganized internally into smaller modules.
1617
* The tests have been reorganized and expanded. Deliverability tests now mostly use captured DNS responses so they can be run off-line.
1718
* The __main__ tool now reads options to validate_email from environment variables.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,8 @@ literal IPv6 addresses if you have allowed them by the `allow_quoted_local`
311311
and `allow_domain_literal` options. In quoted-string local parts, unnecessary
312312
backslash escaping is removed and even the surrounding quotes are removed if
313313
they are unnecessary. For IPv6 domain literals, the IPv6 address is
314-
normalized to condensed form.
314+
normalized to condensed form. [RFC 2142](https://datatracker.ietf.org/doc/html/rfc2142)
315+
also requires lowercase normalization for some specific mailbox names like `postmaster@`.
315316

316317
Examples
317318
--------

email_validator/rfc_constants.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,10 @@
4343
LOCAL_PART_MAX_LENGTH = 64
4444
DNS_LABEL_LENGTH_LIMIT = 63 # in "octets", RFC 1035 2.3.1
4545
DOMAIN_MAX_LENGTH = 255 # in "octets", RFC 1035 2.3.4 and RFC 5321 4.5.3.1.2
46+
47+
# RFC 2142
48+
CASE_INSENSITIVE_MAILBOX_NAMES = [
49+
'info', 'marking', 'sales', 'support', # section 3
50+
'abuse', 'noc', 'security', # section 4
51+
'postmaster', 'hostmaster', 'usenet', 'news', 'webmaster', 'www', 'uucp', 'ftp', # section 5
52+
]

email_validator/validate_email.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from .exceptions_types import EmailSyntaxError, ValidatedEmail
44
from .syntax import validate_email_local_part, validate_email_domain_name, validate_email_domain_literal, get_length_reason
5-
from .rfc_constants import EMAIL_MAX_LENGTH, QUOTED_LOCAL_PART_ADDR
5+
from .rfc_constants import EMAIL_MAX_LENGTH, QUOTED_LOCAL_PART_ADDR, CASE_INSENSITIVE_MAILBOX_NAMES
66

77

88
def validate_email(
@@ -92,6 +92,15 @@ def validate_email(
9292
ret.ascii_local_part = local_part_info["ascii_local_part"]
9393
ret.smtputf8 = local_part_info["smtputf8"]
9494

95+
# Some local parts are required to be case-insensitive, so we should normalize
96+
# to lowercase.
97+
# RFC 2142
98+
if ret.ascii_local_part is not None \
99+
and ret.ascii_local_part.lower() in CASE_INSENSITIVE_MAILBOX_NAMES \
100+
and ret.local_part is not None:
101+
ret.ascii_local_part = ret.ascii_local_part.lower()
102+
ret.local_part = ret.local_part.lower()
103+
95104
# Validate the email address's domain part syntax and get a normalized form.
96105
is_domain_literal = False
97106
if len(domain_part) == 0:

tests/test_syntax.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,11 @@ def test_email_test_domain_name_in_test_environment():
413413
validate_email("anything@mycompany.test", test_environment=True)
414414

415415

416+
def test_case_insensitive_mailbox_name():
417+
validate_email("POSTMASTER@test", test_environment=True).normalized = "postmaster@test"
418+
validate_email("NOT-POSTMASTER@test", test_environment=True).normalized = "NOT-POSTMASTER@test"
419+
420+
416421
# This is the pyIsEmail (https://github.com/michaelherold/pyIsEmail) test suite.
417422
#
418423
# The test data was extracted by:

0 commit comments

Comments
 (0)