From 98f13762d7e18c0ba12044eed56e9198ca056c66 Mon Sep 17 00:00:00 2001 From: Erik Romijn Date: Sat, 22 Feb 2014 16:00:23 +0100 Subject: [PATCH] Fixed #22123 -- EmailValidator now also accepts IPv6 literals in addresses --- django/core/validators.py | 28 +++++++++++++++++++++------- docs/releases/1.7.txt | 3 +++ tests/validators/tests.py | 7 +++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/django/core/validators.py b/django/core/validators.py index 6bbee8e7d2a..83e1a12a2fd 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -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) diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index e3144f3d292..400c5d07597 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -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 ===================================== diff --git a/tests/validators/tests.py b/tests/validators/tests.py index 54e5dfbeed2..b88d71fa68c 100644 --- a/tests/validators/tests.py +++ b/tests/validators/tests.py @@ -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),