diff -r eb04ac3a8327 -r 3f4bdea2abbf iro/validate.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iro/validate.py Thu Sep 27 17:15:46 2012 +0200 @@ -0,0 +1,258 @@ +# Copyright (c) 2012 netzguerilla.net +# +# This file is part of Iro. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +# #Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from twisted.internet import defer + +import re +from decorator import decorator +try: + from inspect import getcallargs +except ImportError: + from .inspect_getcallargs import getcallargs +import types + +from .error import ValidateException, InvalidTel, InvalidMail +from .telnumber import Telnumber + +def vBool(value, field): + '''Validate function for boolean values + + :return: **value** + :raises: :exc:`iro.error.ValidateException` + ''' + t=[True, 1, "true", "True", "TRUE"] + f=[False, 0, "false", "False", "FALSE"] + if value in t: + return True + elif value in f: + return False + else: + raise ValidateException(field=field, msg='%s is not boolean' % field) + +def vNumber(value,field, nval, minv=None, maxv=None, none_allowed=False): + """validate function for integer values. + + :param integer minv: minimum value + :param integer maxv: maximum value + :param func nval: function that give back a number + :param boolean none_allowed: is None or empty string allowed + :return: **value** + :raises: :exc:`iro.error.ValidateException` + """ + if none_allowed and value in [None,'']: + return None + + try: + ret = nval(value) + except ValueError: + raise ValidateException(field=field) + except TypeError: + raise ValidateException(field=field) + + if minv is not None and ret < minv: + raise ValidateException(field=field) + + if maxv is not None and ret > maxv: + raise ValidateException(field=field) + + return ret + +def vInteger(value, field, minv=None, maxv=None, none_allowed=False): + """validate function for integer values. + + :param integer minv: minimum value + :param integer maxv: maximum value + :param boolean none_allowed: is None or empty string allowed + :return: **value** + :raises: :exc:`iro.error.ValidateException` + + see also :func:vNumber + """ + return vNumber(value, field, int, minv, maxv, none_allowed) + +def vFloat(value, field, minv=None, maxv=None, none_allowed=False): + """validate function for float values. + + :param integer minv: minimum value + :param integer maxv: maximum value + :param boolean none_allowed: is None or empty string allowed + :return: **value** + :raises: :exc:`iro.error.ValidateException` + + see also :func:vNumber + """ + return vNumber(value, field, float, minv, maxv, none_allowed) + + + +def vHash(value,field,minlength=None,maxlength=None): + '''Validate function for hash values + + :param integer minlength: minimum length of value string + :param integer maxlength: maximum length of value string + :return: **value** + :raises: :exc:`iro.error.ValidateException` + ''' + if not re.match(r'^[a-f0-9]*$', value.lower()): + raise ValidateException(field=field) + if minlength and len(value)maxlength: + raise ValidateException(field=field) + return value.lower() + +def vTel(value,field): + '''Validator for telefon numbers + :return: **value** + :raises: :exc:`iro.error.InvalidTel` + ''' + + ret = [] + for v in value: + try: + tel=Telnumber(v) + if tel not in ret: + ret.append(tel) + except InvalidTel, e: + e.field=field + raise e + return ret + +def vEmail(value, field, allowString=True, allowList=True): + '''validator for emailadresses (see wikipeda for strange mailadresses and RFC3696) + + valid: + + - "very.(),:;<>[]\\".VERY.\\"very@\\\ \\"very\\".unusual"@strange.example.com + - ""@example.org + - "very.unusual.@.unusual.com"@example.com' + + not valid: + + - Abc.@example.com + - Abc..123@example.com + - thisis."notallowed@example.com + - this\\ still\\"not\\allowed@example.com + + :param boolean allowString: value can be a string -> a string is returned + :param boolean allowList: value is a a list -> a list is returned + :return: **value** + :raises: :exc:`iro.error.ValidateException`, :exc:`iro.error.InvalidMail` + ''' + ret = [] + str_=False + if type(value) is types.StringType: + if not allowString: + raise ValidateException('%s must be a list of email addresses.'%field) + str_=True + value=[value] + elif not allowList: + raise ValidateException('%s must be a email address - No list of email addresses.'%field) + for v in value: + parts= re.match(r'^(.*)@(.+?)$',v) + if not parts: + raise InvalidMail(v,field) + local=parts.group(1) + domain=parts.group(2) + + if not re.match(r'^(\[[0-9\.]{7,16}\]|\[[0-9a-f:]{3,}\]|([a-z0-9+\-%_]+\.)+[a-z]{2,6})$',domain.lower()): + raise InvalidMail(v,field) + + if local == "": + ret.append(v) + continue + + if local.startswith(".") or local.endswith("."): + raise InvalidMail(v,field) + unquote = True + parts = local.split('"') + c=0 + i=0 + for part in parts: + if unquote and part != "": #unquoted is not allowd so much + if not re.match(r'^[^\\,\[\];\(\)@<>: ]+$',part) or ".." in part: + raise InvalidMail(v,field) + if i == 0: + if unquote and part != "" and len(parts) > 1 and part[-1] != '.': #quoted parts must be seperated by a dot + raise InvalidMail(v,field) + unquote = not unquote + c+=1 + elif part == '' or part[-1] != "\\": + if unquote and part != "": #quoted parts must be seperated by a dot + if part[0] != ".": + raise InvalidMail(v,field) + if i < len(parts)-1 and part[-1] != '.': + raise InvalidMail(v,field) + unquote = not unquote + c += 1 + i += 1 + if c%2 == 0 and c > 1: #no single quote allowed + raise InvalidMail(v,field) + if v not in ret: + ret.append(v) + if str_: + ret=ret[0] + return ret + +def validate(kwd,func, need=True,*args,**kargs): + '''validate decorator. + + :param string kwd: keyword to validate + :param func func: validate function + :param boolean need: ``False`` -- ``None`` is a valid value for kwd + :params args: arguments for validate function + :params kargs: keyword arguments for validate function + + .. note:: this decorator can handle function that returns a defer object. + + use it like this:: + + @validate(kwd=userhash, func=vuserhash) + f(userhash) + + that will validate ``userhash`` with the function **vuserhash**. + Every validate function should raise an Exception, if the the value is not valid. + All **args** and **kargs** are used to call the validate function. + if **need** is True, the kwd can't be `None`. + ''' + @decorator + def v(f,*a,**k): + kp=getcallargs(f,*a,**k) + def dfunc(*x,**y): + return None + try: + if kp[kwd] is not None: + dfunc=func + elif need: + raise ValidateException(field=kwd,msg="%s is nessasary"%kwd) + except KeyError: + if need: + raise ValidateException(field=kwd,msg="%s is nessasary"%kwd) + kp[kwd] = None + + def _gotResult(value): + kp[kwd] = value + e = defer.maybeDeferred(f,**kp) + return e + d = defer.maybeDeferred(dfunc, kp[kwd],kwd,*args,**kargs) + return d.addCallback(_gotResult) + return v +