iro/validate.py
author Sandro Knauß <knauss@netzguerilla.net>
Mon, 05 Mar 2012 23:51:56 +0100
branchdevel
changeset 217 d755b2e0cc0b
parent 196 ee2c051fbe3f
child 267 ef2df3f23cb1
permissions -rw-r--r--
task now saves own status

from twisted.internet import defer

import re
from decorator import decorator
from inspect import getcallargs
import types

from .error import ValidateException, InvalidTel, InvalidMail
from .telnumber import Telnumber

def vBool(value, field):
    '''Validator for boolean values'''
    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 vInteger(value, field, minv=None, maxv=None, none_allowed=False):

    if none_allowed and value in [None,'']:
        return None

    try:
        ret = int(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 vHash(value,field,minlength=None,maxlength=None):
    '''Validator for hash values'''
    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 Telefonnumbers'''
    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
    '''
    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
use it like this:
  @validate(kwd=userhash, func=vuserhash)
  f(userhash)
that will validate usrhash 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