Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #4833 -- Validate email addresses with localhost as domain

  • Loading branch information...
commit 4e2e8f39d19d79a59c2696b2c40cb619a54fa745 1 parent 1bbd36a
Claude Paroz claudep authored
65 django/core/validators.py
View
@@ -78,30 +78,53 @@ def validate_integer(value):
raise ValidationError('')
-class EmailValidator(RegexValidator):
+class EmailValidator(object):
+ message = _('Enter a valid e-mail address.')
+ code = 'invalid'
+ user_regex = re.compile(
+ r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*$" # dot-atom
+ 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}\]$',
+ re.IGNORECASE)
+ domain_whitelist = ['localhost']
+
+ def __init__(self, message=None, code=None, whitelist=None):
+ if message is not None:
+ self.message = message
+ if code is not None:
+ self.code = code
+ if whitelist is not None:
+ self.domain_whitelist = whitelist
def __call__(self, value):
- try:
- super(EmailValidator, self).__call__(value)
- except ValidationError as e:
- # Trivial case failed. Try for possible IDN domain-part
- if value and '@' in value:
- parts = value.split('@')
- try:
- parts[-1] = parts[-1].encode('idna').decode('ascii')
- except UnicodeError:
- raise e
- super(EmailValidator, self).__call__('@'.join(parts))
- else:
- raise
+ value = force_text(value)
+
+ if not value or '@' not in value:
+ raise ValidationError(self.message, code=self.code)
+
+ user_part, domain_part = value.split('@', 1)
+
+ if not self.user_regex.match(user_part):
+ raise ValidationError(self.message, code=self.code)
+
+ if (not domain_part in self.domain_whitelist and
+ not self.domain_regex.match(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:
+ return
+ except UnicodeError:
+ pass
+ raise ValidationError(self.message, code=self.code)
-email_re = re.compile(
- r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
- # quoted-string, see also http://tools.ietf.org/html/rfc2822#section-3.2.5
- r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"'
- r')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)$)' # domain
- r'|\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$', re.IGNORECASE) # literal form, ipv4 address (SMTP 4.1.3)
-validate_email = EmailValidator(email_re, _('Enter a valid email address.'), 'invalid')
+validate_email = EmailValidator()
slug_re = re.compile(r'^[-a-zA-Z0-9_]+$')
validate_slug = RegexValidator(slug_re, _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid')
2  docs/ref/validators.txt
View
@@ -96,7 +96,7 @@ to, or in lieu of custom ``field.clean()`` methods.
------------------
.. data:: validate_email
- A :class:`RegexValidator` instance that ensures a value looks like an
+ An ``EmailValidator`` instance that ensures a value looks like an
email address.
``validate_slug``
2  tests/modeltests/validators/tests.py
View
@@ -29,6 +29,8 @@
(validate_email, 'example@valid-----hyphens.com', None),
(validate_email, 'example@valid-with-hyphens.com', None),
(validate_email, 'test@domain.with.idn.tld.उदाहरण.परीक्षा', None),
+ (validate_email, 'email@localhost', None),
+ (EmailValidator(whitelist=['localdomain']), 'email@localdomain', None),
(validate_email, None, ValidationError),
(validate_email, '', ValidationError),
Please sign in to comment.
Something went wrong with that request. Please try again.