Ticket #811: ipv6-standalone.diff

File ipv6-standalone.diff, 16.5 KB (added by Johann Queuniet <johann.queuniet@…>, 18 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