--- /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 <iro@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)<minlength:
+ raise ValidateException(field=field)
+ if maxlength 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
+