# HG changeset patch # User Sandro Knauß # Date 1327902766 -3600 # Node ID 1ac2439a68b58dc0e88d988d50e87ab894d4472d # Parent 19b3f383c9cefc394b49e7d58ee4dde798e10794 adding test function for email- and telefonnumbers. diff -r 19b3f383c9ce -r 1ac2439a68b5 iro/anbieter/telnumber.py --- a/iro/anbieter/telnumber.py Mon Jan 30 06:51:28 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -#Copyright (C) 2009 Sandro Knauß - -#This program is free software; you can redistribute it and/or modify it under the terms -#of the GNU General Public License as published by the Free Software Foundation; -#either version 3 of the License, or any later version. -#This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -#without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -#See the GNU General Public License for more details. - -#You should have received a copy of the GNU General Public License -#along with this program; if not, see . - -class anbieter: - default_conf = '' # override this -import re - -class NotATelNumber(Exception): - def __init__(self, number): - self.number= number - - def __str__(self): - return ("This is not a telefonnumber:", selfnumber) - -class telnumber: - re_telnum=re.compile(r'^\s*(\+)?([0-9\s\-/\(\)])+\s*$') - re_land=re.compile(r'^\s*(\+|00)(?P[1-9]{2})') - re_number=re.compile(r'[^0-9]') - std_land="49" - - def __init__(self,number=None): - if not(number is None): - self.createNumber(number) - - def createNumber(self, number): - - if not self.re_telnum.match(number): - raise NotATelNumber(number) - - - self.land=self.std_land - land=self.re_land.match(number) - - if not(land is None): - self.land=land.group("land") - number=number[land.end("land"):] - - number=self.re_number.sub('',number) - - if number[0]=="0": - number=number[1:] - - self.number = number - - def __eq__(self, y): - return ((self.number == y.number) and ( self.land == y.land)) - - def __ne__(self, y): - return not self.__eq__(y) diff -r 19b3f383c9ce -r 1ac2439a68b5 iro/controller/viewinterface.py --- a/iro/controller/viewinterface.py Mon Jan 30 06:51:28 2012 +0100 +++ b/iro/controller/viewinterface.py Mon Jan 30 06:52:46 2012 +0100 @@ -160,4 +160,26 @@ ''' return "" + @validate(kwd="recipients",func=vTel) + def telnumber(self,recipients): + '''Gibt True zurück, falls alle übergebene Telefonnummern valide sind. + + Keywords: + recipients[list]: Eine Liste von Emfänger-Nummern (gemäß ITU-T E.123) + Return: + True: alle übergebene Nummern sind valide + ''' + return True + + @validate(kwd="recipients",func=vEmail) + def email(self,recipients): + '''Gibt True zurück, falls alle übergebene Emailadressen valide sind. + + Keywords: + recipients[list]: Eine Liste von Emailadressen + + Return: + True: alle übergebene Nummern sind valide + ''' + return True diff -r 19b3f383c9ce -r 1ac2439a68b5 iro/error.py --- a/iro/error.py Mon Jan 30 06:51:28 2012 +0100 +++ b/iro/error.py Mon Jan 30 06:52:46 2012 +0100 @@ -40,4 +40,12 @@ def __str__(self): return "%i:%s"%(self.code,self.msg) - +class NotATelNumber(ValidateException): + def __init__(self, number,field=None): + msg = "No valid telnumber: '%s'"%(number) + ValidateException.__init__(self, 701, field, msg) + +class InvalidMail(ValidateException): + def __init__(self, number,field=None): + msg = "No valid email: '%s'"%(number) + ValidateException.__init__(self, 702, field, msg) diff -r 19b3f383c9ce -r 1ac2439a68b5 iro/telnumber.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iro/telnumber.py Mon Jan 30 06:52:46 2012 +0100 @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +#Copyright (C) 2009 Sandro Knauß + +#This program is free software; you can redistribute it and/or modify it under the terms +#of the GNU General Public License as published by the Free Software Foundation; +#either version 3 of the License, or any later version. +#This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +#without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +#See the GNU General Public License for more details. + +#You should have received a copy of the GNU General Public License +#along with this program; if not, see . + +import re + +from .error import NotATelNumber + +class Telnumber: + re_telnum=re.compile(r'^\s*(\+)?([0-9\s\-/\(\)]){5,}\s*$') + re_land=re.compile(r'^\s*(\+|00)(?P[1-9]{2})') + re_number=re.compile(r'[^0-9]') + std_land="49" + + def __init__(self,number=None): + if not(number is None): + self.createNumber(number) + + def createNumber(self, number): + + if not self.re_telnum.match(number): + raise NotATelNumber(number) + + + self.land=self.std_land + land=self.re_land.match(number) + + if land: + self.land=land.group("land") + number=number[land.end("land"):] + + number=self.re_number.sub('',number) + + if number[0]=="0": + number=number[1:] + + self.number = number + + def __eq__(self, y): + return ((self.number == y.number) and ( self.land == y.land)) + + def __ne__(self, y): + return not self.__eq__(y) diff -r 19b3f383c9ce -r 1ac2439a68b5 iro/validate.py --- a/iro/validate.py Mon Jan 30 06:51:28 2012 +0100 +++ b/iro/validate.py Mon Jan 30 06:52:46 2012 +0100 @@ -1,10 +1,11 @@ +from twisted.internet import defer + import re from decorator import decorator - -from twisted.internet import defer +from inspect import getcallargs -from inspect import getcallargs -from .error import ValidateException +from .error import ValidateException, NotATelNumber, InvalidMail +from .telnumber import Telnumber def vBool(value, field): '''Validator for boolean values''' @@ -29,10 +30,70 @@ return value def vTel(value,field): - return value + '''Validator for Telefonnumbers''' + ret = [] + for v in value: + try: + ret.append(Telnumber(v)) + except NotATelNumber, e: + e.field=field + raise e + return ret def vEmail(value, field): - return value + '''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 = [] + 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 == "": + 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) + ret.append(v) + return ret def validate(kwd,func, need=True,*args,**kargs): '''validate decorator diff -r 19b3f383c9ce -r 1ac2439a68b5 iro/view/xmlrpc.py --- a/iro/view/xmlrpc.py Mon Jan 30 06:51:28 2012 +0100 +++ b/iro/view/xmlrpc.py Mon Jan 30 06:52:46 2012 +0100 @@ -31,7 +31,7 @@ """Since we override lookupProcedure, its suggested to override listProcedures too. """ - return ['listMethods','status','stop','sms','fax','mail','routes','defaultRoute','statistic'] + return ['listMethods','status','stop','sms','fax','mail','routes','defaultRoute','statistic','telnumber','email'] class XMLRPCInterface(TwistedInterface,xmlrpc.XMLRPC): diff -r 19b3f383c9ce -r 1ac2439a68b5 tests/xmlrpc.py --- a/tests/xmlrpc.py Mon Jan 30 06:51:28 2012 +0100 +++ b/tests/xmlrpc.py Mon Jan 30 06:52:46 2012 +0100 @@ -69,7 +69,7 @@ def testListMethods(self): '''list of all offical Methods, that can be executed''' ret=self.__rpc2().listMethods() - self.failUnlessEqual(ret, ['listMethods', 'status', 'stop', 'sms', 'fax', 'mail', 'routes', 'defaultRoute', 'statistic']) + self.failUnlessEqual(ret, ['listMethods', 'status', 'stop', 'sms', 'fax', 'mail', 'routes', 'defaultRoute', 'statistic', 'telnumber','email']) def testStatus(self): ''' test the status function''' @@ -171,8 +171,64 @@ session.add(u) self.failUnlessEqual(self.__rpc2().defaultRoute('abcdef123456789','sms'),['sipgate_basic']) + def testTelnumbers(self): + '''test the telefon validator''' + self.failUnlessEqual(self.__rpc2().telnumber(["0123/456(78)","+4912346785433","00123435456-658"]),True) + numbers=['xa','+1','1-23',';:+0','0123'] + for number in numbers: + with self.assertRaises(Fault) as fault: + self.__rpc2().telnumber([number]) + exc = fault.exception + self.failUnlessEqual(exc.faultCode, 701) + self.failUnlessEqual(exc.faultString, "No valid telnumber: '%s'"%number) + + with self.assertRaises(Fault) as fault: + self.__rpc2().telnumber(['01234']+numbers) + exc = fault.exception + self.failUnlessEqual(exc.faultCode, 701) + self.failUnlessEqual(exc.faultString, "No valid telnumber: '%s'"%numbers[0]) + def testVaildEmail(self): + '''test vaild email adresses (got from wikipedia)''' + validmails=["niceandsimple@example.com", + "simplewith+symbol@example.com", + 'a.little.unusual@example.com', + 'a.little.more.unusual@dept.example.com', + '@[10.10.10.10]', + '@[1.1.1.1]', + '@[200.100.100.100]', + 'user@[2001:db8:1ff::a0b:dbd0]', + '"much.more\ unusual"@example.com', + '"very.unusual.@.unusual.com"@example.com', + '"very.(),:;<>[]\\".VERY.\\"very@\\\ \\"very\\".unusual"@strange.example.com', + "!#$%&'*+-/=?^_`{}|~@example.org", + '"()<>[]:;@,\\\"!#$%&\'*+-/=?^_`{}| ~ ? ^_`{}|~."@example.org', + '""@example.org'] + + for num in validmails: + self.failUnlessEqual(self.__rpc2().email([num]),True) + + def testInvaildEmail(self): + '''test invaild email adresses (got from wikipedia)''' + invalid=["Abc.example.com", # (an @ character must separate the local and domain parts) + "Abc.@example.com", # (character dot(.) is last in local part) + "Abc..123@example.com", # (character dot(.) is double) + "A@b@c@example.com", # (only one @ is allowed outside quotation marks) + 'a"b(c)d,e:f;gi[j\k]l@example.com', # (none of the special characters in this local part is allowed outside quotation marks) + 'just"not"right@example.com', # (quoted strings must be dot separated, or the only element making up the local-part) + 'thisis."notallowed@example.com', # (spaces, quotes, and backslashes may only exist when within quoted strings and preceded by a slash) + 'this\\ still\\"not\\allowed@example.com', # (even if escaped (preceded by a backslash), spaces, quotes, and backslashes must still be contained by quotes) + ] + + for number in invalid: + with self.assertRaises(Fault) as fault: + self.__rpc2().email([number]) + exc = fault.exception + self.failUnlessEqual(exc.faultCode, 702) + self.failUnlessEqual(exc.faultString, "No valid email: '%s'"%number) + + def startReactor(engine): """starts the Rector with a special debug Clild, so that the reactor can be stopped remotly. """ from twisted.internet import reactor