Ticket #811: ipv6-standalone.diff

File ipv6-standalone.diff, 16.5 KB (added by Johann Queuniet <johann.queuniet@…>, 16 years ago)

Same as the 02/02/08 patch, without IPy

  • django/oldforms/__init__.py

     
    933933
    934934    def isValidIPAddress(self, field_data, all_data):
    935935        try:
    936             validators.isValidIPAddress4(field_data, all_data)
     936            validators.isValidIP4Address(field_data, all_data)
    937937        except validators.ValidationError, e:
    938938            raise validators.CriticalValidationError, e.messages
    939939
     
    941941        return data or None
    942942    html2python = staticmethod(html2python)
    943943
     944class IP6AddressField(IPAddressField):
     945    def __init__(self, field_name, length=39, max_length=39, is_required=False, validator_list=None):
     946        if validator_list is None: validator_list = []
     947        validator_list = [self.isValidIPAddress] + validator_list
     948        TextField.__init__(self, field_name, length=length, max_length=max_length,
     949            is_required=is_required, validator_list=validator_list)
     950
     951    def isValidIPAddress(self, field_data, all_data):
     952        try:
     953            validators.isValidIPAddress(field_data, all_data)
     954        except validators.ValidationError, e:
     955            raise validators.CriticalValidationError, e.messages
     956
     957    def html2python(data):
     958        return data or None
     959    html2python = staticmethod(html2python)
     960
    944961####################
    945962# MISCELLANEOUS    #
    946963####################
  • django/db/models/fields/__init__.py

     
    943943        defaults.update(kwargs)
    944944        return super(IPAddressField, self).formfield(**defaults)
    945945
     946class IP6AddressField(Field):
     947    empty_strings_allowed = False
     948    def __init__(self, *args, **kwargs):
     949        kwargs['max_length'] = 39
     950        Field.__init__(self, *args, **kwargs)
     951
     952    def get_manipulator_field_objs(self):
     953        return [oldforms.IP6AddressField]
     954
     955    def validate(self, field_data, all_data):
     956        validators.isValidIPAddress(field_data, None)
     957
     958    def formfield(self, **kwargs):
     959        defaults = {'form_class': forms.IP6AddressField}
     960        defaults.update(kwargs)
     961        return super(IP6AddressField, self).formfield(**defaults)
     962
     963
    946964class NullBooleanField(Field):
    947965    empty_strings_allowed = False
    948966    def __init__(self, *args, **kwargs):
  • django/db/backends/postgresql/introspection.py

     
    7575    23: 'IntegerField',
    7676    25: 'TextField',
    7777    701: 'FloatField',
    78     869: 'IPAddressField',
     78    869: 'IP6AddressField',
    7979    1043: 'CharField',
    8080    1082: 'DateField',
    8181    1083: 'TimeField',
  • django/db/backends/postgresql/creation.py

     
    1616    'ImageField':        'varchar(%(max_length)s)',
    1717    'IntegerField':      'integer',
    1818    'IPAddressField':    'inet',
     19    'IP6AddressField':   'inet',
    1920    'NullBooleanField':  'boolean',
    2021    'OneToOneField':     'integer',
    2122    'PhoneNumberField':  'varchar(20)',
  • django/db/backends/mysql_old/creation.py

     
    1616    'ImageField':        'varchar(%(max_length)s)',
    1717    'IntegerField':      'integer',
    1818    'IPAddressField':    'char(15)',
     19    'IP6AddressField':   'char(39)',
    1920    'NullBooleanField':  'bool',
    2021    'OneToOneField':     'integer',
    2122    'PhoneNumberField':  'varchar(20)',
  • django/db/backends/sqlite3/creation.py

     
    1515    'ImageField':                   'varchar(%(max_length)s)',
    1616    'IntegerField':                 'integer',
    1717    'IPAddressField':               'char(15)',
     18    'IP6AddressField':              'char(39)',
    1819    'NullBooleanField':             'bool',
    1920    'OneToOneField':                'integer',
    2021    'PhoneNumberField':             'varchar(20)',
  • django/db/backends/mysql/creation.py

     
    1616    'ImageField':        'varchar(%(max_length)s)',
    1717    'IntegerField':      'integer',
    1818    'IPAddressField':    'char(15)',
     19    'IPAddressField':    'char(39)',
    1920    'NullBooleanField':  'bool',
    2021    'OneToOneField':     'integer',
    2122    'PhoneNumberField':  'varchar(20)',
  • django/db/backends/oracle/creation.py

     
    1919    'ImageField':                   'NVARCHAR2(%(max_length)s)',
    2020    'IntegerField':                 'NUMBER(11)',
    2121    'IPAddressField':               'VARCHAR2(15)',
     22    'IP6AddressField':              'VARCHAR2(39)',
    2223    'NullBooleanField':             'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))',
    2324    'OneToOneField':                'NUMBER(11)',
    2425    'PhoneNumberField':             'VARCHAR2(20)',
  • django/db/backends/postgresql_psycopg2/introspection.py

     
    7272    23: 'IntegerField',
    7373    25: 'TextField',
    7474    701: 'FloatField',
    75     869: 'IPAddressField',
     75    869: 'IP6AddressField',
    7676    1043: 'CharField',
    7777    1082: 'DateField',
    7878    1083: 'TimeField',
  • django/core/validators.py

     
    1919from django.utils.translation import ugettext as _, ugettext_lazy, ungettext
    2020from django.utils.functional import Promise, lazy
    2121from django.utils.encoding import force_unicode, smart_str
     22from django.utils.http import ip6_normalize
    2223
    2324_datere = r'\d{4}-\d{1,2}-\d{1,2}'
    2425_timere = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?'
     
    3334    r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain
    3435integer_re = re.compile(r'^-?\d+$')
    3536ip4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
     37ip6_re = re.compile(r'^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$')
    3638phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE)
    3739slug_re = re.compile(r'^[-\w]+$')
    3840url_re = re.compile(r'^https?://\S+$')
     
    106108        except ValidationError:
    107109            raise ValidationError, _("Enter valid e-mail addresses separated by commas.")
    108110
    109 def isValidIPAddress4(field_data, all_data):
     111def isValidIP4Address(field_data, all_data):
    110112    if not ip4_re.search(field_data):
    111         raise ValidationError, _("Please enter a valid IP address.")
     113        raise ValidationError, _("Please enter a valid IPv4 address.")
    112114
     115def isValidIPAddress(field_data, all_data):
     116    if not ip4_re.search(field_data):
     117        try:
     118            ip = ip6_normalize(field_data)
     119            if not ip6_re.search(ip):
     120                raise ValidationError, _("Please enter a valid IP address.")
     121        except ValueError:
     122            raise ValidationError, _("Please enter a valid IP address.")
     123
    113124def isNotEmpty(field_data, all_data):
    114125    if field_data.strip() == '':
    115126        raise ValidationError, _("Empty values are not allowed here.")
  • django/newforms/fields.py

     
    1919
    2020from django.utils.translation import ugettext_lazy as _
    2121from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str
     22from django.utils.http import ip6_normalize
    2223
    2324from util import ErrorList, ValidationError
    2425from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput
     
    3233    'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
    3334    'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
    3435    'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
    35     'SplitDateTimeField', 'IPAddressField', 'FilePathField',
     36    'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'IP6AddressField',
    3637)
    3738
    3839# These values, if given to to_python(), will trigger the self.required check.
     
    782783
    783784    def __init__(self, *args, **kwargs):
    784785        super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs)
     786
     787ipv6_re = re.compile(r'^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$')
     788
     789class IP6AddressField(CharField):
     790    def __init__(self, *args, **kwargs):
     791        super(IP6AddressField, self).__init__(39, 3, *args, **kwargs)
     792
     793    def clean(self, value):
     794        value = super(IP6AddressField, self).clean(value)
     795        if value == u'':
     796            return value
     797        if not ipv4_re.search(value):
     798            try:
     799                ip = ip6_normalize(value)
     800                if not ipv6_re.search(ip):
     801                    raise ValidationError(_(u'Enter a valid IP address.'))
     802            except ValueError:
     803                raise ValidationError(_(u'Enter a valid IP address.'))
     804        return value
  • django/utils/http.py

     
    6565    """
    6666    rfcdate = formatdate(epoch_seconds)
    6767    return '%s GMT' % rfcdate[:25]
     68
     69def ip6_normalize(addr):
     70    """
     71    Normalize an IPv6 address to allow easy regexp validation
     72    mostly checks the length, and gets ride of tricky things
     73    like IPv4 mapped addresses and :: shortcuts
     74   
     75    Outputs a string
     76    """
     77    # Some basic error checking
     78    if addr.count('::') > 2 or ':::' in addr:
     79        raise ValueError
     80
     81    ip = addr.split(':')
     82    nbfull = len([elem for elem in ip if elem != ''])
     83    nb = len(ip)
     84
     85    if nbfull >= 1 and '.' in ip[-1]:
     86        # Convert IPv4 mapped addresses to full hexa
     87        ipv4 = ip[-1].split('.')
     88        hex1 = (int(ipv4[0]) << 8) + int(ipv4[1])
     89        hex2 = (int(ipv4[2]) << 8) + int(ipv4[3])
     90        ip[-1:] = [hex(hex1)[2:], hex(hex2)[2:]]
     91        nbfull = nbfull + 1
     92        nb = nb + 1
     93
     94    if nbfull == 8 or nbfull == nb:
     95        # No need to bother
     96        return addr
     97    elif nbfull > 8:
     98        # Has to be invalid anyway
     99        raise ValueError
     100
     101    # Begin normalization
     102    start, end, index = (None, None, 0)
     103    for elem in ip:
     104        if elem == '':
     105            if start is None:
     106                start = index
     107                end = index
     108            else:
     109                end = index
     110        index += 1
     111    pad = 8 - nbfull
     112    if end != start:
     113        ip[start:end-start+1] = ['0'] * pad
     114    else:
     115        ip[start] = '0'
     116        if pad > 1:
     117            ip[start:1] = ['0'] * (pad - 1)
     118    return ':'.join([item for item in ip if len(item) > 0])
  • tests/regressiontests/forms/extra.py

     
    377377...
    378378ValidationError: [u'Enter a valid IPv4 address.']
    379379
     380# IP6AddressField ##################################################################
     381
     382>>> f = IP6AddressField()
     383>>> f.clean('')
     384Traceback (most recent call last):
     385...
     386ValidationError: [u'This field is required.']
     387>>> f.clean(None)
     388Traceback (most recent call last):
     389...
     390ValidationError: [u'This field is required.']
     391>>> f.clean('127.0.0.1')
     392u'127.0.0.1'
     393>>> f.clean('2a01:05d8:25ae:9451:020d:39bc:21e6:8cab')
     394u'2a01:05d8:25ae:9451:020d:39bc:21e6:8cab'
     395>>> f.clean('2a01:5d8:25ae:9451:20d:39bc:21e6:8cab')
     396u'2a01:5d8:25ae:9451:20d:39bc:21e6:8cab'
     397>>> f.clean('::1')
     398u'::1'
     399>>> f.clean('::ffff:88.191.32.1')
     400u'::ffff:88.191.32.1'
     401>>> f.clean('foo')
     402Traceback (most recent call last):
     403...
     404ValidationError: [u'Enter a valid IP address.']
     405>>> f.clean('127.0.0.')
     406Traceback (most recent call last):
     407...
     408ValidationError: [u'Enter a valid IP address.']
     409>>> f.clean('::1:')
     410Traceback (most recent call last):
     411...
     412ValidationError: [u'Enter a valid IP address.']
     413>>> f.clean('1.2.3.4.5')
     414Traceback (most recent call last):
     415...
     416ValidationError: [u'Enter a valid IP address.']
     417>>> f.clean('2a01:5d8:25ae:9451:20d:39bc:21e6:8cab:fb2c')
     418Traceback (most recent call last):
     419...
     420ValidationError: [u'Ensure this value has at most 39 characters (it has 42).']
     421>>> f.clean('2a01:5d8:25ae:9451:20d:39bc:1e6:cab:b2c')
     422Traceback (most recent call last):
     423...
     424ValidationError: [u'Enter a valid IP address.']
     425>>> f.clean('256.125.1.5')
     426Traceback (most recent call last):
     427...
     428ValidationError: [u'Enter a valid IP address.']
     429>>> f.clean('::12345')
     430Traceback (most recent call last):
     431...
     432ValidationError: [u'Enter a valid IP address.']
     433
     434>>> f = IP6AddressField(required=False)
     435>>> f.clean('')
     436u''
     437>>> f.clean(None)
     438u''
     439>>> f.clean('127.0.0.1')
     440u'127.0.0.1'
     441>>> f.clean('foo')
     442Traceback (most recent call last):
     443...
     444ValidationError: [u'Enter a valid IP address.']
     445>>> f.clean('127.0.0.')
     446Traceback (most recent call last):
     447...
     448ValidationError: [u'Enter a valid IP address.']
     449>>> f.clean('1.2.3.4.5')
     450Traceback (most recent call last):
     451...
     452ValidationError: [u'Enter a valid IP address.']
     453>>> f.clean('256.125.1.5')
     454Traceback (most recent call last):
     455...
     456ValidationError: [u'Enter a valid IP address.']
     457
     458
    380459#################################
    381460# Tests of underlying functions #
    382461#################################
  • docs/modelforms.txt

     
    5858    ``ImageField``                   ``ImageField``
    5959    ``IntegerField``                 ``IntegerField``
    6060    ``IPAddressField``               ``IPAddressField``
     61    ``IP6AddressField``               ``IP6AddressField``
    6162    ``ManyToManyField``              ``ModelMultipleChoiceField`` (see
    6263                                     below)
    6364    ``NullBooleanField``             ``CharField``
  • docs/model-api.txt

     
    388388
    389389The admin represents this as an ``<input type="text">`` (a single-line input).
    390390
     391``IP6AddressField``
     392~~~~~~~~~~~~~~~~~~
     393
     394An IPv6 or IPv4 address, in string format (i.e. "24.124.1.30",
     395"2002:58bf:278c::1", "::ffff:88.191.52.1").
     396
     397The admin represents this as an ``<input type="text">`` (a single-line input).
     398
    391399``NullBooleanField``
    392400~~~~~~~~~~~~~~~~~~~~
    393401
  • docs/newforms.txt

     
    14271427      expression.
    14281428    * Error message keys: ``required``, ``invalid``
    14291429
     1430``IP6AddressField``
     1431~~~~~~~~~~~~~~~~~~
     1432
     1433    * Default widget: ``TextInput``
     1434    * Empty value: ``''`` (an empty string)
     1435    * Normalizes to: A Unicode object.
     1436    * Validates that the given value is a valid IPv4 or IPv6 address
     1437    * Error message keys: ``required``, ``invalid``
     1438
    14301439``MultipleChoiceField``
    14311440~~~~~~~~~~~~~~~~~~~~~~~
    14321441
  • docs/form_for_model.txt

     
    6565    ``ImageField``                   ``ImageField``
    6666    ``IntegerField``                 ``IntegerField``
    6767    ``IPAddressField``               ``IPAddressField``
     68    ``IP6AddressField``               ``IP6AddressField``
    6869    ``ManyToManyField``              ``ModelMultipleChoiceField`` (see
    6970                                     below)
    7071    ``NullBooleanField``             ``CharField``
Back to Top