adding test function for email- and telefonnumbers. devel
authorSandro Knauß <knauss@netzguerilla.net>
Mon, 30 Jan 2012 06:52:46 +0100
branchdevel
changeset 126 1ac2439a68b5
parent 125 19b3f383c9ce
child 127 79966b937274
adding test function for email- and telefonnumbers.
iro/anbieter/telnumber.py
iro/controller/viewinterface.py
iro/error.py
iro/telnumber.py
iro/validate.py
iro/view/xmlrpc.py
tests/xmlrpc.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ß <bugs@sandroknauss.de>
-
-#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 <http://www.gnu.org/licenses/>.
-
-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<land>[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)
--- 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
--- 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)
--- /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ß <bugs@sandroknauss.de>
+
+#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 <http://www.gnu.org/licenses/>.
+
+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<land>[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)
--- 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
--- 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): 
--- 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;g<h>i[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