Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #9764 - Updated EmailField and URLField to support IDN (Interna…

…tionalized Domain Names). Thanks, UloPe.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12474 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 7f5d9ad661d65a869cd72b736250936a942d5d2b 1 parent 0d4726f
Jannis Leidel authored February 21, 2010
44  django/core/validators.py
... ...
@@ -1,4 +1,5 @@
1 1
 import re
  2
+import urlparse
2 3
 
3 4
 from django.core.exceptions import ValidationError
4 5
 from django.utils.translation import ugettext_lazy as _
@@ -52,7 +53,29 @@ def __init__(self, verify_exists=False, validator_user_agent=URL_VALIDATOR_USER_
52 53
         self.user_agent = validator_user_agent
53 54
 
54 55
     def __call__(self, value):
55  
-        super(URLValidator, self).__call__(value)
  56
+        try:
  57
+            super(URLValidator, self).__call__(value)
  58
+        except ValidationError, e:
  59
+            # Trivial case failed. Try for possible IDN domain
  60
+            if value:
  61
+                original = value
  62
+                value = smart_unicode(value)
  63
+                splitted = urlparse.urlsplit(value)
  64
+                try:
  65
+                    netloc_ace = splitted[1].encode('idna') # IDN -> ACE
  66
+                except UnicodeError: # invalid domain part
  67
+                    raise e
  68
+                value = value.replace(splitted[1], netloc_ace)
  69
+                # If no URL path given, assume /
  70
+                if not splitted[2]:
  71
+                    value += u'/'
  72
+                super(URLValidator, self).__call__(value)
  73
+                # After validation revert ACE encoded domain-part to
  74
+                # original (IDN) value as suggested by RFC 3490
  75
+                value = original
  76
+            else:
  77
+                raise
  78
+
56 79
         if self.verify_exists:
57 80
             import urllib2
58 81
             headers = {
@@ -77,12 +100,29 @@ def validate_integer(value):
77 100
     except (ValueError, TypeError), e:
78 101
         raise ValidationError('')
79 102
 
  103
+class EmailValidator(RegexValidator):
  104
+
  105
+    def __call__(self, value):
  106
+        try:
  107
+            super(EmailValidator, self).__call__(value)
  108
+        except ValidationError, e:
  109
+            # Trivial case failed. Try for possible IDN domain-part
  110
+            if value and u'@' in value:
  111
+                parts = value.split(u'@')
  112
+                domain_part = parts[-1]
  113
+                try:
  114
+                    parts[-1] = parts[-1].encode('idna')
  115
+                except UnicodeError:
  116
+                    raise e
  117
+                super(EmailValidator, self).__call__(u'@'.join(parts))
  118
+            else:
  119
+                raise
80 120
 
81 121
 email_re = re.compile(
82 122
     r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom
83 123
     r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
84 124
     r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE)  # domain
85  
-validate_email = RegexValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid')
  125
+validate_email = EmailValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid')
86 126
 
87 127
 slug_re = re.compile(r'^[-\w]+$')
88 128
 validate_slug = RegexValidator(slug_re, _(u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid')
11  docs/ref/forms/fields.txt
@@ -482,6 +482,11 @@ Has two optional arguments for validation, ``max_length`` and ``min_length``.
482 482
 If provided, these arguments ensure that the string is at most or at least the
483 483
 given length.
484 484
 
  485
+.. versionchanged:: 1.2
  486
+   The EmailField previously did not recognize e-mail addresses as valid that
  487
+   contained an IDN (Internationalized Domain Name; a domain containing
  488
+   unicode characters) domain part. This has now been corrected.
  489
+
485 490
 ``FileField``
486 491
 ~~~~~~~~~~~~~
487 492
 
@@ -707,6 +712,12 @@ Takes the following optional arguments:
707 712
     String used as the user-agent used when checking for a URL's existence.
708 713
     Defaults to the value of the ``URL_VALIDATOR_USER_AGENT`` setting.
709 714
 
  715
+.. versionchanged:: 1.2
  716
+   The URLField previously did not recognize URLs as valid that contained an IDN
  717
+   (Internationalized Domain Name; a domain name containing unicode characters)
  718
+   domain name. This has now been corrected.
  719
+
  720
+
710 721
 Slightly complex built-in ``Field`` classes
711 722
 -------------------------------------------
712 723
 
3  tests/regressiontests/forms/fields.py
@@ -408,6 +408,8 @@ def test_emailfield_32(self):
408 408
         self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid e-mail address.']", f.clean, 'example@inv-.-alid.com')
409 409
         self.assertEqual(u'example@valid-----hyphens.com', f.clean('example@valid-----hyphens.com'))
410 410
         self.assertEqual(u'example@valid-with-hyphens.com', f.clean('example@valid-with-hyphens.com'))
  411
+        self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid e-mail address.']", f.clean, 'example@.com')
  412
+        self.assertEqual(u'local@domain.with.idn.xyz\xe4\xf6\xfc\xdfabc.part.com', f.clean('local@domain.with.idn.xyzäöüßabc.part.com'))
411 413
 
412 414
     def test_email_regexp_for_performance(self):
413 415
         f = EmailField()
@@ -489,6 +491,7 @@ def test_urlfield_37(self):
489 491
         self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://inv-.alid-.com')
490 492
         self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://inv-.-alid.com')
491 493
         self.assertEqual(u'http://valid-----hyphens.com/', f.clean('http://valid-----hyphens.com'))
  494
+        self.assertEqual(u'http://some.idn.xyz\xe4\xf6\xfc\xdfabc.domain.com:123/blah', f.clean('http://some.idn.xyzäöüßabc.domain.com:123/blah'))
492 495
 
493 496
     def test_url_regex_ticket11198(self):
494 497
         f = URLField()

0 notes on commit 7f5d9ad

Please sign in to comment.
Something went wrong with that request. Please try again.