# 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
[docs]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)
[docs]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
[docs]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)
[docs]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)
[docs]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()
[docs]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
[docs]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
[docs]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