iro/validate.py
changeset 302 3f4bdea2abbf
parent 294 0e75bd39767d
child 312 42fd5075a5d1
equal deleted inserted replaced
90:eb04ac3a8327 302:3f4bdea2abbf
       
     1 # Copyright (c) 2012 netzguerilla.net <iro@netzguerilla.net>
       
     2 # 
       
     3 # This file is part of Iro.
       
     4 # 
       
     5 # Permission is hereby granted, free of charge, to any person obtaining a copy of
       
     6 # this software and associated documentation files (the "Software"), to deal in
       
     7 # the Software without restriction, including without limitation the rights to use,
       
     8 # copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
       
     9 # #Software, and to permit persons to whom the Software is furnished to do so,
       
    10 # subject to the following conditions:
       
    11 # 
       
    12 # The above copyright notice and this permission notice shall be included in
       
    13 # all copies or substantial portions of the Software.
       
    14 # 
       
    15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       
    16 # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       
    17 # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       
    18 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       
    19 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       
    20 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       
    21 
       
    22 from twisted.internet import defer
       
    23 
       
    24 import re
       
    25 from decorator import decorator
       
    26 try:
       
    27     from inspect import getcallargs
       
    28 except ImportError:
       
    29     from .inspect_getcallargs import getcallargs
       
    30 import types
       
    31 
       
    32 from .error import ValidateException, InvalidTel, InvalidMail
       
    33 from .telnumber import Telnumber
       
    34 
       
    35 def vBool(value, field):
       
    36     '''Validate function  for boolean values
       
    37     
       
    38     :return: **value**
       
    39     :raises: :exc:`iro.error.ValidateException`
       
    40     '''
       
    41     t=[True, 1, "true", "True", "TRUE"]
       
    42     f=[False, 0, "false", "False", "FALSE"]
       
    43     if value in t:
       
    44         return True
       
    45     elif value in f:
       
    46         return False
       
    47     else:
       
    48         raise ValidateException(field=field, msg='%s is not boolean' % field)
       
    49 
       
    50 def vNumber(value,field, nval,  minv=None, maxv=None, none_allowed=False):
       
    51     """validate function for integer values.
       
    52 
       
    53     :param integer minv: minimum value
       
    54     :param integer maxv: maximum value
       
    55     :param func nval: function that give back a number
       
    56     :param boolean none_allowed: is None or empty string allowed
       
    57     :return: **value**
       
    58     :raises: :exc:`iro.error.ValidateException`
       
    59     """
       
    60     if none_allowed and value in [None,'']:
       
    61         return None
       
    62 
       
    63     try:
       
    64         ret = nval(value)
       
    65     except ValueError:
       
    66         raise ValidateException(field=field)
       
    67     except TypeError:
       
    68         raise ValidateException(field=field)
       
    69 
       
    70     if minv is not None and ret < minv:
       
    71         raise ValidateException(field=field)
       
    72 
       
    73     if maxv is not None and ret > maxv:
       
    74         raise ValidateException(field=field)
       
    75 
       
    76     return ret
       
    77 
       
    78 def vInteger(value, field, minv=None, maxv=None, none_allowed=False):
       
    79     """validate function for integer values.
       
    80 
       
    81     :param integer minv: minimum value
       
    82     :param integer maxv: maximum value
       
    83     :param boolean none_allowed: is None or empty string allowed
       
    84     :return: **value**
       
    85     :raises: :exc:`iro.error.ValidateException`
       
    86 
       
    87     see also :func:vNumber
       
    88     """
       
    89     return vNumber(value, field, int, minv, maxv, none_allowed)
       
    90 
       
    91 def vFloat(value, field, minv=None, maxv=None, none_allowed=False):
       
    92     """validate function for float values.
       
    93 
       
    94     :param integer minv: minimum value
       
    95     :param integer maxv: maximum value
       
    96     :param boolean none_allowed: is None or empty string allowed
       
    97     :return: **value**
       
    98     :raises: :exc:`iro.error.ValidateException`
       
    99 
       
   100     see also :func:vNumber
       
   101     """
       
   102     return vNumber(value, field, float, minv, maxv, none_allowed)
       
   103 
       
   104 
       
   105 
       
   106 def vHash(value,field,minlength=None,maxlength=None):
       
   107     '''Validate function for hash values
       
   108     
       
   109     :param integer minlength: minimum length of value string
       
   110     :param integer maxlength: maximum length of value string
       
   111     :return: **value**
       
   112     :raises: :exc:`iro.error.ValidateException`
       
   113     '''
       
   114     if not re.match(r'^[a-f0-9]*$', value.lower()):
       
   115         raise ValidateException(field=field)
       
   116     if minlength and len(value)<minlength:
       
   117         raise ValidateException(field=field)
       
   118     if maxlength and len(value)>maxlength:
       
   119         raise ValidateException(field=field)
       
   120     return value.lower()
       
   121 
       
   122 def vTel(value,field):
       
   123     '''Validator for telefon numbers
       
   124     :return: **value**
       
   125     :raises: :exc:`iro.error.InvalidTel`
       
   126     '''
       
   127 
       
   128     ret = []
       
   129     for v in value:
       
   130         try:
       
   131             tel=Telnumber(v)
       
   132             if tel not in ret:
       
   133                 ret.append(tel)
       
   134         except InvalidTel, e:
       
   135             e.field=field
       
   136             raise e
       
   137     return ret
       
   138 
       
   139 def vEmail(value, field, allowString=True, allowList=True):
       
   140     '''validator for emailadresses (see wikipeda for strange mailadresses and RFC3696)
       
   141     
       
   142     valid:
       
   143 
       
   144     - "very.(),:;<>[]\\".VERY.\\"very@\\\ \\"very\\".unusual"@strange.example.com
       
   145     - ""@example.org
       
   146     - "very.unusual.@.unusual.com"@example.com'
       
   147 
       
   148     not valid:
       
   149     
       
   150     - Abc.@example.com
       
   151     - Abc..123@example.com
       
   152     - thisis."notallowed@example.com
       
   153     - this\\ still\\"not\\allowed@example.com
       
   154    
       
   155     :param boolean allowString: value can be a string -> a string is returned
       
   156     :param boolean allowList: value is a a list -> a list is returned
       
   157     :return: **value**
       
   158     :raises: :exc:`iro.error.ValidateException`, :exc:`iro.error.InvalidMail`
       
   159     '''
       
   160     ret = []
       
   161     str_=False
       
   162     if type(value) is types.StringType:
       
   163         if not allowString:
       
   164             raise ValidateException('%s must be a list of email addresses.'%field)
       
   165         str_=True
       
   166         value=[value]
       
   167     elif not allowList:
       
   168         raise ValidateException('%s must be a email address - No list of email addresses.'%field)
       
   169     for v in value:
       
   170         parts= re.match(r'^(.*)@(.+?)$',v)
       
   171         if not parts:
       
   172             raise InvalidMail(v,field)
       
   173         local=parts.group(1)
       
   174         domain=parts.group(2)
       
   175         
       
   176         if not re.match(r'^(\[[0-9\.]{7,16}\]|\[[0-9a-f:]{3,}\]|([a-z0-9+\-%_]+\.)+[a-z]{2,6})$',domain.lower()):
       
   177             raise InvalidMail(v,field)
       
   178        
       
   179         if local == "":
       
   180             ret.append(v)
       
   181             continue
       
   182     
       
   183         if local.startswith(".") or local.endswith("."):
       
   184             raise InvalidMail(v,field)
       
   185         unquote = True
       
   186         parts = local.split('"')
       
   187         c=0
       
   188         i=0
       
   189         for part in parts:
       
   190             if unquote and part != "":                     #unquoted is not allowd so much
       
   191                 if not re.match(r'^[^\\,\[\];\(\)@<>: ]+$',part) or ".." in part:
       
   192                     raise InvalidMail(v,field)
       
   193             if i == 0:
       
   194                 if unquote and part != "" and len(parts) > 1 and part[-1] != '.': #quoted parts must be seperated by a dot
       
   195                     raise InvalidMail(v,field)
       
   196                 unquote = not unquote
       
   197                 c+=1
       
   198             elif part == ''  or part[-1] != "\\":
       
   199                 if unquote and part != "":         #quoted parts must be seperated by a dot
       
   200                     if part[0] != ".":
       
   201                         raise InvalidMail(v,field)
       
   202                     if i < len(parts)-1 and part[-1] != '.':
       
   203                         raise InvalidMail(v,field)
       
   204                 unquote = not unquote
       
   205                 c += 1
       
   206             i += 1
       
   207         if c%2 == 0 and c > 1:                        #no single quote allowed
       
   208             raise InvalidMail(v,field)
       
   209         if v not in ret:
       
   210             ret.append(v)
       
   211     if str_:
       
   212         ret=ret[0]
       
   213     return ret
       
   214 
       
   215 def validate(kwd,func, need=True,*args,**kargs):
       
   216     '''validate decorator.
       
   217 
       
   218     :param string kwd: keyword to validate
       
   219     :param func func: validate function
       
   220     :param boolean need: ``False`` -- ``None`` is a valid value for kwd
       
   221     :params args: arguments for validate function
       
   222     :params kargs: keyword arguments for validate function
       
   223 
       
   224     .. note:: this decorator can handle function that returns a defer object.
       
   225     
       
   226     use it like this::
       
   227 
       
   228       @validate(kwd=userhash, func=vuserhash)
       
   229       f(userhash)
       
   230     
       
   231     that will validate ``userhash`` with the function **vuserhash**.
       
   232     Every validate function should raise an Exception, if the the value is not valid.
       
   233     All **args** and **kargs** are used to call the validate function.
       
   234     if **need** is True, the kwd can't be `None`.
       
   235     '''
       
   236     @decorator
       
   237     def v(f,*a,**k):
       
   238         kp=getcallargs(f,*a,**k)
       
   239         def dfunc(*x,**y):
       
   240             return None
       
   241         try:
       
   242             if kp[kwd] is not None:
       
   243                 dfunc=func
       
   244             elif need:
       
   245                 raise ValidateException(field=kwd,msg="%s is nessasary"%kwd)
       
   246         except KeyError:
       
   247             if need:
       
   248                 raise ValidateException(field=kwd,msg="%s is nessasary"%kwd)
       
   249             kp[kwd] = None
       
   250 
       
   251         def _gotResult(value):
       
   252             kp[kwd] = value
       
   253             e = defer.maybeDeferred(f,**kp)
       
   254             return e
       
   255         d = defer.maybeDeferred(dfunc, kp[kwd],kwd,*args,**kargs)
       
   256         return d.addCallback(_gotResult)
       
   257     return v
       
   258