Fixed #22123 -- EmailValidator now also accepts IPv6 literals in addresses

This commit is contained in:
Erik Romijn 2014-02-22 16:00:23 +01:00 committed by Baptiste Mispelon
parent 173aa51997
commit 98f13762d7
3 changed files with 31 additions and 7 deletions

View File

@ -109,9 +109,11 @@ class EmailValidator(object):
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"$)', # quoted-string
re.IGNORECASE)
domain_regex = re.compile(
r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,})$' # domain
# literal form, ipv4 address (SMTP 4.1.3)
r'|^\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$',
r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,})$',
re.IGNORECASE)
literal_regex = re.compile(
# literal form, ipv4 or ipv6 address (SMTP 4.1.3)
r'\[([A-f0-9:\.]+)\]$',
re.IGNORECASE)
domain_whitelist = ['localhost']
@ -135,18 +137,30 @@ class EmailValidator(object):
raise ValidationError(self.message, code=self.code)
if (not domain_part in self.domain_whitelist and
not self.domain_regex.match(domain_part)):
not self.validate_domain_part(domain_part)):
# Try for possible IDN domain-part
try:
domain_part = domain_part.encode('idna').decode('ascii')
if not self.domain_regex.match(domain_part):
raise ValidationError(self.message, code=self.code)
else:
if self.validate_domain_part(domain_part):
return
except UnicodeError:
pass
raise ValidationError(self.message, code=self.code)
def validate_domain_part(self, domain_part):
if self.domain_regex.match(domain_part):
return True
literal_match = self.literal_regex.match(domain_part)
if literal_match:
ip_address = literal_match.group(1)
try:
validate_ipv46_address(ip_address)
return True
except ValidationError:
pass
return False
def __eq__(self, other):
return isinstance(other, EmailValidator) and (self.domain_whitelist == other.domain_whitelist) and (self.message == other.message) and (self.code == other.code)

View File

@ -760,6 +760,9 @@ Validators
``schemes`` argument which allows customization of the accepted URI schemes
(instead of the defaults ``http(s)`` and ``ftp(s)``).
* :func:`~django.core.validators.validate_email` now accepts addresses with
IPv6 literals, like ``example@[2001:db8::1]``, as specified in RFC 5321.
Backwards incompatible changes in 1.7
=====================================

View File

@ -33,6 +33,9 @@ TEST_DATA = (
(validate_email, 'email@here.com', None),
(validate_email, 'weirder-email@here.and.there.com', None),
(validate_email, 'email@[127.0.0.1]', None),
(validate_email, 'email@[2001:dB8::1]', None),
(validate_email, 'email@[2001:dB8:0:0:0:0:0:1]', None),
(validate_email, 'email@[::fffF:127.0.0.1]', None),
(validate_email, 'example@valid-----hyphens.com', None),
(validate_email, 'example@valid-with-hyphens.com', None),
(validate_email, 'test@domain.with.idn.tld.उदाहरण.परीक्षा', None),
@ -49,6 +52,10 @@ TEST_DATA = (
(validate_email, 'abc@.com', ValidationError),
(validate_email, 'something@@somewhere.com', ValidationError),
(validate_email, 'email@127.0.0.1', ValidationError),
(validate_email, 'email@[127.0.0.256]', ValidationError),
(validate_email, 'email@[2001:db8::12345]', ValidationError),
(validate_email, 'email@[2001:db8:0:0:0:0:1]', ValidationError),
(validate_email, 'email@[::ffff:127.0.0.256]', ValidationError),
(validate_email, 'example@invalid-.com', ValidationError),
(validate_email, 'example@-invalid.com', ValidationError),
(validate_email, 'example@inv-.alid-.com', ValidationError),