0001## FormEncode, a  Form processor
0002## Copyright (C) 2003, Ian Bicking <ianb@colorstudy.com>
0003##  
0004## This library is free software; you can redistribute it and/or
0005## modify it under the terms of the GNU Lesser General Public
0006## License as published by the Free Software Foundation; either
0007## version 2.1 of the License, or (at your option) any later version.
0008##
0009## This library is distributed in the hope that it will be useful,
0010## but WITHOUT ANY WARRANTY; without even the implied warranty of
0011## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012## Lesser General Public License for more details.
0013##
0014## You should have received a copy of the GNU Lesser General Public
0015## License along with this library; if not, write to the Free Software
0016## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0017##
0018## NOTE: In the context of the Python environment, I interpret "dynamic
0019## linking" as importing -- thus the LGPL applies to the contents of
0020## the modules, but make no requirements on code importing these
0021## modules.
0022"""
0023Validator/Converters for use with FormEncode.
0024"""
0025
0026import re
0027DateTime = None
0028httplib = None
0029urlparse = None
0030import socket
0031from interfaces import *
0032from api import *
0033sha = random = None
0034try:
0035    import sets
0036except ImportError:
0037    sets = None
0038
0039import cgi
0040
0041import fieldstorage
0042
0043try:
0044    import DNS
0045    DNS.DiscoverNameServers()
0046    have_dns=True
0047except ImportError:
0048    have_dns=False
0049
0050True, False = (1==1), (0==1)
0051
0052def _(s): return s # dummy translation function, nothing is translated here.
0053                   # Instead this is actually done in api.message.
0054                   # The surrounding _("string") of the strings is only for extracting
0055                   # the strings automatically
0056                   # if you run pygettext with this source comment this function out temporarly 
0057
0058############################################################
0059## Utility methods
0060############################################################
0061
0062# These all deal with accepting both mxDateTime and datetime
0063# modules and types
0064datetime_module = None
0065mxDateTime_module = None
0066
0067def import_datetime(module_type):
0068    global datetime_module, mxDateTime_module
0069    if module_type is None:
0070        try:
0071            if datetime_module is None:
0072                import datetime as datetime_module
0073            return datetime_module
0074        except ImportError:
0075            if mxDateTime_module is None:
0076                from mx import DateTime as mxDateTime_module
0077            return mxDateTime_module
0078
0079    module_type = module_type.lower()
0080    assert module_type in ('datetime', 'mxdatetime')
0081    if module_type == 'datetime':
0082        if datetime_module is None:
0083            import datetime as datetime_module
0084        return datetime_module
0085    else:
0086        if mxDateTime_module is None:
0087            from mx import DateTime as mxDateTime_module
0088        return mxDateTime_module
0089
0090def datetime_now(module):
0091    if module.__name__ == 'datetime':
0092        return module.datetime.now()
0093    else:
0094        return module.now()
0095
0096def datetime_makedate(module, year, month, day):
0097    if module.__name__ == 'datetime':
0098        return module.date(year, month, day)
0099    else:
0100        try:
0101            return module.DateTime(year, month, day)
0102        except module.RangeError, e:
0103            raise ValueError(str(e))
0104
0105
0106# TODO: Needs being extended to support mx.DateTime as well.
0107def datetime_time(module):
0108    if module.__name__ == 'datetime':
0109        return module.time
0110
0111# TODO: Needs being extended to support mx.DateTime as well.
0112def datetime_isotime(module):
0113    if module.__name__ == 'datetime':
0114        return module.time.isoformat
0115
0116
0117
0118############################################################
0119## Wrapper Validators
0120############################################################
0121
0122class ConfirmType(FancyValidator):
0123
0124    """
0125    Confirms that the input/output is of the proper type.
0126
0127    Uses the parameters:
0128
0129    subclass:
0130        The class or a tuple of classes; the item must be an instance
0131        of the class or a subclass.
0132    type:
0133        A type or tuple of types (or classes); the item must be of
0134        the exact class or type.  Subclasses are not allowed.
0135
0136    Examples::
0137    
0138        >>> cint = ConfirmType(subclass=int)
0139        >>> cint.to_python(True)
0140        True
0141        >>> cint.to_python('1')
0142        Traceback (most recent call last):
0143            ...
0144        Invalid: '1' is not a subclass of <type 'int'>
0145        >>> cintfloat = ConfirmType(subclass=(float, int))
0146        >>> cintfloat.to_python(1.0), cintfloat.from_python(1.0)
0147        (1.0, 1.0)
0148        >>> cintfloat.to_python(1), cintfloat.from_python(1)
0149        (1, 1)
0150        >>> cintfloat.to_python(None)
0151        Traceback (most recent call last):
0152            ...
0153        Invalid: None is not a subclass of one of the types <type 'float'>, <type 'int'>
0154        >>> cint2 = ConfirmType(type=int)
0155        >>> cint2(accept_python=False).from_python(True)
0156        Traceback (most recent call last):
0157            ...
0158        Invalid: True must be of the type <type 'int'>
0159    """
0160
0161    subclass = None
0162    type = None
0163
0164    messages = {
0165        'subclass': _("%(object)r is not a subclass of %(subclass)s"),
0166        'inSubclass': _("%(object)r is not a subclass of one of the types %(subclassList)s"),
0167        'inType': _("%(object)r must be one of the types %(typeList)s"),
0168        'type': _("%(object)r must be of the type %(type)s"),
0169        }
0170
0171    def __init__(self, *args, **kw):
0172        FancyValidator.__init__(self, *args, **kw)
0173        if self.subclass:
0174            if isinstance(self.subclass, list):
0175                self.subclass = tuple(self.subclass)
0176            elif not isinstance(self.subclass, tuple):
0177                self.subclass = (self.subclass,)
0178            self.validate_python = self.confirm_subclass
0179        if self.type:
0180            if isinstance(self.type, list):
0181                self.type = tuple(self.type)
0182            elif not isinstance(self.type, tuple):
0183                self.type = (self.type,)
0184            self.validate_python = self.confirm_type
0185
0186    def confirm_subclass(self, value, state):
0187        if not isinstance(value, self.subclass):
0188            if len(self.subclass) == 1:
0189                msg = self.message('subclass', state, object=value,
0190                                   subclass=self.subclass[0])
0191            else:
0192                subclass_list = ', '.join(map(str, self.subclass))
0193                msg = self.message('inSubclass', state, object=value,
0194                                   subclassList=subclass_list)
0195            raise Invalid(msg, value, state)
0196
0197    def confirm_type(self, value, state):
0198        for t in self.type:
0199            if type(value) is t:
0200                break
0201        else:
0202            if len(self.type) == 1:
0203                msg = self.message('type', state, object=value,
0204                                   type=self.type[0])
0205            else:
0206                msg = self.message('inType', state, object=value,
0207                                   typeList=', '.join(map(str, self.type)))
0208            raise Invalid(msg, value, state)
0209        return value
0210
0211    def is_empty(self, value):
0212        return False
0213
0214class Wrapper(FancyValidator):
0215
0216    """
0217    Used to convert functions to validator/converters.
0218
0219    You can give a simple function for `to_python`, `from_python`,
0220    `validate_python` or `validate_other`.  If that function raises an
0221    exception, the value is considered invalid.  Whatever value the
0222    function returns is considered the converted value.
0223
0224    Unlike validators, the `state` argument is not used.  Functions
0225    like `int` can be used here, that take a single argument.
0226
0227    Examples::
0228
0229        >>> def downcase(v):
0230        ...     return v.lower()
0231        >>> wrap = Wrapper(to_python=downcase)
0232        >>> wrap.to_python('This')
0233        'this'
0234        >>> wrap.from_python('This')
0235        'This'
0236        >>> wrap2 = Wrapper(from_python=downcase)
0237        >>> wrap2.from_python('This')
0238        'this'
0239        >>> wrap2.from_python(1)
0240        Traceback (most recent call last):
0241          ...
0242        Invalid: 'int' object has no attribute 'lower'
0243        >>> wrap3 = Wrapper(validate_python=int)
0244        >>> wrap3.to_python('1')
0245        '1'
0246        >>> wrap3.to_python('a')
0247        Traceback (most recent call last):
0248          ...
0249        Invalid: invalid literal for int(): a
0250    """
0251
0252    func_to_python = None
0253    func_from_python = None
0254    func_validate_python = None
0255    func_validate_other = None
0256
0257    def __init__(self, *args, **kw):
0258        for n in ['to_python', 'from_python', 'validate_python',
0259                  'validate_other']:
0260            if kw.has_key(n):
0261                kw['func_%s' % n] = kw[n]
0262                del kw[n]
0263        FancyValidator.__init__(self, *args, **kw)
0264        self._to_python = self.wrap(self.func_to_python)
0265        self._from_python = self.wrap(self.func_from_python)
0266        self.validate_python = self.wrap(self.func_validate_python)
0267        self.validate_other = self.wrap(self.func_validate_other)
0268
0269    def wrap(self, func):
0270        if not func:
0271            return None
0272        def result(value, state, func=func):
0273            try:
0274                return func(value)
0275            except Exception, e:
0276                raise Invalid(str(e), {}, value, state)
0277        return result
0278
0279class Constant(FancyValidator):
0280
0281    """
0282    This converter converts everything to the same thing.
0283
0284    I.e., you pass in the constant value when initializing, then all
0285    values get converted to that constant value.
0286
0287    This is only really useful for funny situations, like::
0288
0289      fromEmailValidator = ValidateAny(
0290                               ValidEmailAddress(),
0291                               Constant('unknown@localhost'))
0292                               
0293    In this case, the if the email is not valid
0294    ``'unknown@localhost'`` will be used instead.  Of course, you
0295    could use ``if_invalid`` instead.
0296
0297    Examples::
0298
0299        >>> Constant('X').to_python('y')
0300        'X'
0301    """
0302
0303    __unpackargs__ = ('value',)
0304
0305    def _to_python(self, value, state):
0306        return self.value
0307
0308    _from_python = _to_python
0309
0310############################################################
0311## Normal validators
0312############################################################
0313
0314class MaxLength(FancyValidator):
0315
0316    """
0317    Invalid if the value is longer than `maxLength`.  Uses len(),
0318    so it can work for strings, lists, or anything with length.
0319
0320    Examples::
0321
0322        >>> max5 = MaxLength(5)
0323        >>> max5.to_python('12345')
0324        '12345'
0325        >>> max5.from_python('12345')
0326        '12345'
0327        >>> max5.to_python('123456')
0328        Traceback (most recent call last):
0329          ...
0330        Invalid: Enter a value less than 5 characters long
0331        >>> max5(accept_python=False).from_python('123456')
0332        Traceback (most recent call last):
0333          ...
0334        Invalid: Enter a value less than 5 characters long
0335        >>> max5.to_python([1, 2, 3])
0336        [1, 2, 3]
0337        >>> max5.to_python([1, 2, 3, 4, 5, 6])
0338        Traceback (most recent call last):
0339          ...
0340        Invalid: Enter a value less than 5 characters long
0341        >>> max5.to_python(5)
0342        Traceback (most recent call last):
0343          ...
0344        Invalid: Invalid value (value with length expected)
0345    """
0346
0347    __unpackargs__ = ('maxLength',)
0348    messages = {
0349        'tooLong': _("Enter a value less than %(maxLength)i characters long"),
0350        'invalid': _("Invalid value (value with length expected)"),
0351        }
0352
0353    def validate_python(self, value, state):
0354        try:
0355            if value and                  len(value) > self.maxLength:
0357                raise Invalid(self.message('tooLong', state,
0358                                           maxLength=self.maxLength),
0359                              value, state)
0360            else:
0361                return None
0362        except TypeError:
0363            raise Invalid(self.message('invalid', state),
0364                          value, state)
0365
0366class MinLength(FancyValidator):
0367
0368    """
0369    Invalid if the value is shorter than `minlength`.  Uses len(),
0370    so it can work for strings, lists, or anything with length.
0371
0372    Examples::
0373
0374        >>> min5 = MinLength(5)
0375        >>> min5.to_python('12345')
0376        '12345'
0377        >>> min5.from_python('12345')
0378        '12345'
0379        >>> min5.to_python('1234')
0380        Traceback (most recent call last):
0381          ...
0382        Invalid: Enter a value at least 5 characters long
0383        >>> min5(accept_python=False).from_python('1234')
0384        Traceback (most recent call last):
0385          ...
0386        Invalid: Enter a value at least 5 characters long
0387        >>> min5.to_python([1, 2, 3, 4, 5])
0388        [1, 2, 3, 4, 5]
0389        >>> min5.to_python([1, 2, 3])
0390        Traceback (most recent call last):
0391          ...
0392        Invalid: Enter a value at least 5 characters long
0393        >>> min5.to_python(5)
0394        Traceback (most recent call last):
0395          ...
0396        Invalid: Invalid value (value with length expected)
0397        
0398    """
0399
0400    __unpackargs__ = ('minLength',)
0401
0402    messages = {
0403        'tooShort': _("Enter a value at least %(minLength)i characters long"),
0404        'invalid': _("Invalid value (value with length expected)"),
0405        }
0406
0407    def validate_python(self, value, state):
0408        try:
0409            if len(value) < self.minLength:
0410                raise Invalid(self.message('tooShort', state,
0411                                           minLength=self.minLength),
0412                              value, state)
0413        except TypeError:
0414            raise Invalid(self.message('invalid', state),
0415                          value, state)
0416
0417class NotEmpty(FancyValidator):
0418
0419    """
0420    Invalid if value is empty (empty string, empty list, etc).
0421
0422    Generally for objects that Python considers false, except zero
0423    which is not considered invalid.
0424
0425    Examples::
0426
0427        >>> ne = NotEmpty(messages={'empty': 'enter something'})
0428        >>> ne.to_python('')
0429        Traceback (most recent call last):
0430          ...
0431        Invalid: enter something
0432        >>> ne.to_python(0)
0433        0
0434    """
0435    not_empty = True
0436
0437    messages = {
0438        'empty': _("Please enter a value"),
0439        }
0440
0441    def validate_python(self, value, state):
0442        if value == 0:
0443            # This isn't "empty" for this definition.
0444            return value
0445        if not value:
0446            raise Invalid(self.message('empty', state),
0447                          value, state)
0448
0449class Empty(FancyValidator):
0450
0451    """
0452    Invalid unless the value is empty.  Use cleverly, if at all.
0453
0454    Examples::
0455
0456        >>> Empty.to_python(0)
0457        Traceback (most recent call last):
0458          ...
0459        Invalid: You cannot enter a value here
0460    """
0461
0462    messages = {
0463        'notEmpty': _("You cannot enter a value here"),
0464        }
0465
0466    def validate_python(self, value, state):
0467        if value or value == 0:
0468            raise Invalid(self.message('notEmpty', state),
0469                          value, state)
0470
0471class Regex(FancyValidator):
0472
0473    """
0474    Invalid if the value doesn't match the regular expression `regex`.
0475
0476    The regular expression can be a compiled re object, or a string
0477    which will be compiled for you.
0478
0479    Use strip=True if you want to strip the value before validation,
0480    and as a form of conversion (often useful).
0481
0482    Examples::
0483
0484        >>> cap = Regex(r'^[A-Z]+$')
0485        >>> cap.to_python('ABC')
0486        'ABC'
0487
0488    Note that ``.from_python()`` calls (in general) do not validate
0489    the input::
0490    
0491        >>> cap.from_python('abc')
0492        'abc'
0493        >>> cap(accept_python=False).from_python('abc')
0494        Traceback (most recent call last):
0495          ...
0496        Invalid: The input is not valid
0497        >>> cap.to_python(1)
0498        Traceback (most recent call last):
0499          ...
0500        Invalid: The input must be a string (not a <type 'int'>: 1)
0501        >>> Regex(r'^[A-Z]+$', strip=True).to_python('  ABC  ')
0502        'ABC'
0503        >>> Regex(r'this', regexOps=('I',)).to_python('THIS')
0504        'THIS'
0505    """
0506
0507    regexOps = ()
0508    strip = False
0509    regex = None
0510
0511    __unpackargs__ = ('regex',)
0512
0513    messages = {
0514        'invalid': _("The input is not valid"),
0515        }
0516
0517    def __init__(self, *args, **kw):
0518        FancyValidator.__init__(self, *args, **kw)
0519        if isinstance(self.regex, basestring):
0520            ops = 0
0521            assert not isinstance(self.regexOps, basestring), (
0522                "regexOps should be a list of options from the re module "
0523                "(names, or actual values)")
0524            for op in self.regexOps:
0525                if isinstance(op, basestring):
0526                    ops |= getattr(re, op)
0527                else:
0528                    ops |= op
0529            self.regex = re.compile(self.regex, ops)
0530
0531    def validate_python(self, value, state):
0532        self.assert_string(value, state)
0533        if self.strip and isinstance(value, basestring):
0534            value = value.strip()
0535        if not self.regex.search(value):
0536            raise Invalid(self.message('invalid', state),
0537                          value, state)
0538
0539    def _to_python(self, value, state):
0540        if self.strip and isinstance(value, basestring):
0541            return value.strip()
0542        return value
0543
0544class PlainText(Regex):
0545
0546    """
0547    Test that the field contains only letters, numbers, underscore,
0548    and the hyphen.  Subclasses Regex.
0549
0550    Examples::
0551
0552        >>> PlainText.to_python('_this9_')
0553        '_this9_'
0554        >>> PlainText.from_python('  this  ')
0555        '  this  '
0556        >>> PlainText(accept_python=False).from_python('  this  ')
0557        Traceback (most recent call last):
0558          ...
0559        Invalid: Enter only letters, numbers, or _ (underscore)
0560        >>> PlainText(strip=True).to_python('  this  ')
0561        'this'
0562        >>> PlainText(strip=True).from_python('  this  ')
0563        'this'
0564    """
0565
0566    regex = r"^[a-zA-Z_\-0-9]*$"
0567
0568    messages = {
0569        'invalid': _('Enter only letters, numbers, or _ (underscore)'),
0570        }
0571
0572class OneOf(FancyValidator):
0573
0574    """
0575    Tests that the value is one of the members of a given list.
0576
0577    If ``testValueList=True``, then if the input value is a list or
0578    tuple, all the members of the sequence will be checked (i.e., the
0579    input must be a subset of the allowed values).
0580
0581    Use ``hideList=True`` to keep the list of valid values out of the
0582    error message in exceptions.
0583
0584    Examples::
0585
0586        >>> oneof = OneOf([1, 2, 3])
0587        >>> oneof.to_python(1)
0588        1
0589        >>> oneof.to_python(4)
0590        Traceback (most recent call last):
0591          ...
0592        Invalid: Value must be one of: 1; 2; 3 (not 4)
0593        >>> oneof(testValueList=True).to_python([2, 3, [1, 2, 3]])
0594        [2, 3, [1, 2, 3]]
0595        >>> oneof.to_python([2, 3, [1, 2, 3]])
0596        Traceback (most recent call last):
0597