Commit 98f13762 authored by Erik Romijn's avatar Erik Romijn Committed by Baptiste Mispelon
Browse files

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

parent 173aa519
Loading
Loading
Loading
Loading
+21 −7
Original line number Diff line number Diff line
@@ -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)

+3 −0
Original line number Diff line number Diff line
@@ -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
=====================================

+7 −0
Original line number Diff line number Diff line
@@ -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),