==== Patch <sip-fixes> level 3
Source: f7f26e75-0421-0410-8811-d6d8ddf80b0f:/local/twisted:305 [local]
Target: bbbe8e31-12d6-0310-92fd-ac37d47ddeeb:/trunk:18594 [mirrored]
        (svn://svn.twistedmatrix.com/svn/Twisted/trunk)
Log:
 r296@antoine-ubuntu:  antoine | 2006-10-30 11:33:09 +0100
 creating local branch
 
 r297@antoine-ubuntu:  antoine | 2006-10-30 12:16:54 +0100
 bug fixes + tests
 
 r303@antoine-ubuntu:  antoine | 2006-11-03 18:46:59 +0100
 redo multiline header fix
 
 r304@antoine-ubuntu:  antoine | 2006-11-03 18:53:30 +0100
 Via parsing as class method
 
 r305@antoine-ubuntu:  antoine | 2006-11-06 10:46:56 +0100
 test case for unknown params in Via header
 

=== twisted/protocols/sip.py
==================================================================
--- twisted/protocols/sip.py	(revision 18594)
+++ twisted/protocols/sip.py	(patch sip-fixes level 3)
@@ -94,7 +94,7 @@
     488: "Not Acceptable Here",
     491: "Request Pending",
     493: "Undecipherable",
-    
+
     500: "Internal Server Error",
     501: "Not Implemented",
     502: "Bad Gateway", # no donut
@@ -102,7 +102,7 @@
     504: "Server Time-out",
     505: "SIP Version not supported",
     513: "Message Too Large",
-    
+
     600: "Busy Everywhere",
     603: "Decline",
     604: "Does not exist anywhere",
@@ -167,7 +167,7 @@
         m.update(":")
         m.update(pszHEntity)
     HA2 = m.digest().encode('hex')
-    
+
     m = md5.md5()
     m.update(HA1)
     m.update(":")
@@ -185,10 +185,10 @@
     return hash
 
 class Via:
-    """A SIP Via header."""
+    """A SIP Via header. Unknown parameters are ignored."""
 
     def __init__(self, host, port=PORT, transport="UDP", ttl=None, hidden=False,
-                 received=None, rport=None, branch=None, maddr=None):
+                 received=None, rport=None, branch=None, maddr=None, **kargs):
         self.transport = transport
         self.host = host
         self.port = port
@@ -205,51 +205,53 @@
             s += ";hidden"
         for n in "ttl", "branch", "maddr", "received", "rport":
             value = getattr(self, n)
-            if value == True:
+            if value is True:
                 s += ";" + n
             elif value != None:
                 s += ";%s=%s" % (n, value)
         return s
 
-
-def parseViaHeader(value):
-    """Parse a Via header, returning Via class instance."""
-    parts = value.split(";")
-    sent, params = parts[0], parts[1:]
-    protocolinfo, by = sent.split(" ", 1)
-    by = by.strip()
-    result = {}
-    pname, pversion, transport = protocolinfo.split("/")
-    if pname != "SIP" or pversion != "2.0":
-        raise ValueError, "wrong protocol or version: %r" % value
-    result["transport"] = transport
-    if ":" in by:
-        host, port = by.split(":")
-        result["port"] = int(port)
-        result["host"] = host
-    else:
-        result["host"] = by
-    for p in params:
-        # it's the comment-striping dance!
-        p = p.strip().split(" ", 1)
-        if len(p) == 1:
-            p, comment = p[0], ""
+    def fromString(cls, value):
+        """Parse a Via header, returning Via class instance."""
+        parts = value.split(";")
+        sent, params = parts[0], parts[1:]
+        protocolinfo, by = sent.split(" ", 1)
+        by = by.strip()
+        result = {}
+        pname, pversion, transport = protocolinfo.split("/")
+        if pname != "SIP" or pversion != "2.0":
+            raise ValueError, "wrong protocol or version: %r" % value
+        result["transport"] = transport
+        if ":" in by:
+            host, port = by.split(":")
+            result["port"] = int(port)
+            result["host"] = host
         else:
-            p, comment = p
-        if p == "hidden":
-            result["hidden"] = True
-            continue
-        parts = p.split("=", 1)
-        if len(parts) == 1:
-            name, value = parts[0], True
-        else:
-            name, value = parts
-            if name in ("rport", "ttl"):
-                value = int(value)
-        result[name] = value
-    return Via(**result)
+            result["host"] = by
+        for p in params:
+            # it's the comment-striping dance!
+            p = p.strip().split(" ", 1)
+            if len(p) == 1:
+                p, comment = p[0], ""
+            else:
+                p, comment = p
+            if p == "hidden":
+                result["hidden"] = True
+                continue
+            parts = p.split("=", 1)
+            if len(parts) == 1:
+                name, value = parts[0], True
+            else:
+                name, value = parts
+                if name in ("rport", "ttl"):
+                    value = int(value)
+            result[name] = value
+        return cls(**result)
 
+    fromString = classmethod(fromString)
 
+parseViaHeader = Via.fromString
+
 class URL:
     """A SIP URL."""
 
@@ -301,7 +303,7 @@
 
     def __str__(self):
         return self.toString()
-    
+
     def __repr__(self):
         return '<URL %s:%s@%s:%r/%s>' % (self.username, self.password, self.host, self.port, self.transport)
 
@@ -419,12 +421,12 @@
     """A SIP message."""
 
     length = None
-    
+
     def __init__(self):
         self.headers = util.OrderedDict() # map name to list of values
         self.body = ""
         self.finished = 0
-    
+
     def addHeader(self, name, value):
         name = name.lower()
         name = longHeaders.get(name, name)
@@ -434,7 +436,7 @@
 
     def bodyDataReceived(self, data):
         self.body += data
-    
+
     def creationFinished(self):
         if (self.length != None) and (self.length != len(self.body)):
             raise ValueError, "wrong body length"
@@ -465,7 +467,7 @@
         else:
             self.uri = parseURL(uri)
             cleanRequestURL(self.uri)
-    
+
     def __repr__(self):
         return "<SIP Request %d:%s %s>" % (id(self), self.method, self.uri.toString())
 
@@ -501,9 +503,9 @@
     acceptResponses = 1
     acceptRequests = 1
     state = "firstline" # or "headers", "body" or "invalid"
-    
+
     debug = 0
-    
+
     def __init__(self, messageReceivedCallback):
         self.messageReceived = messageReceivedCallback
         self.reset()
@@ -513,12 +515,13 @@
         self.length = None # body length
         self.bodyReceived = 0 # how much of the body we received
         self.message = None
+        self.header = None
         self.setLineMode(remainingData)
-    
+
     def invalidMessage(self):
         self.state = "invalid"
         self.setRawMode()
-    
+
     def dataDone(self):
         # clear out any buffered data that may be hanging around
         self.clearLineBuffer()
@@ -530,27 +533,27 @@
         if self.length == None:
             # no content-length header, so end of data signals message done
             self.messageDone()
-        elif self.length < self.bodyReceived:
+        elif self.length > self.bodyReceived:
             # aborted in the middle
             self.reset()
         else:
             # we have enough data and message wasn't finished? something is wrong
             raise RuntimeError, "this should never happen"
-    
+
     def dataReceived(self, data):
         try:
             basic.LineReceiver.dataReceived(self, data)
         except:
             log.err()
             self.invalidMessage()
-    
+
     def handleFirstLine(self, line):
         """Expected to create self.message."""
         raise NotImplementedError
 
     def lineLengthExceeded(self, line):
         self.invalidMessage()
-    
+
     def lineReceived(self, line):
         if self.state == "firstline":
             while line.startswith("\n") or line.startswith("\r"):
@@ -580,24 +583,36 @@
         else:
             assert self.state == "headers"
         if line:
-            # XXX support multi-line headers
-            try:
-                name, value = line.split(":", 1)
-            except ValueError:
-                self.invalidMessage()
-                return
-            self.message.addHeader(name, value.lstrip())
-            if name.lower() == "content-length":
+            # multi-line header
+            if line.startswith(" ") or line.startswith("\t"):
+                name, value = self.header
+                self.header = name, (value + line.lstrip())
+            else:
+                # new header
+                if self.header:
+                    self.message.addHeader(*self.header)
+                    self.header = None
                 try:
-                    self.length = int(value.lstrip())
+                    name, value = line.split(":", 1)
                 except ValueError:
                     self.invalidMessage()
                     return
+                self.header = name, value.lstrip()
+                # XXX we assume content-length won't be multiline
+                if name.lower() == "content-length":
+                    try:
+                        self.length = int(value.lstrip())
+                    except ValueError:
+                        self.invalidMessage()
+                        return
         else:
             # CRLF, we now have message body until self.length bytes,
             # or if no length was given, until there is no more data
             # from the connection sending us data.
             self.state = "body"
+            if self.header:
+                self.message.addHeader(*self.header)
+                self.header = None
             if self.length == 0:
                 self.messageDone()
                 return
@@ -608,7 +623,7 @@
         self.message.creationFinished()
         self.messageReceived(self.message)
         self.reset(remainingData)
-    
+
     def rawDataReceived(self, data):
         assert self.state in ("body", "invalid")
         if self.state == "invalid":
@@ -631,10 +646,10 @@
 
 class Base(protocol.DatagramProtocol):
     """Base class for SIP clients and servers."""
-    
+
     PORT = PORT
     debug = False
-    
+
     def __init__(self):
         self.messages = []
         self.parser = MessagesParser(self.addMessage)
@@ -658,7 +673,7 @@
     def _fixupNAT(self, message, (srcHost, srcPort)):
         # RFC 2543 6.40.2,
         senderVia = parseViaHeader(message.headers["via"][0])
-        if senderVia.host != srcHost:            
+        if senderVia.host != srcHost:
             senderVia.received = srcHost
             if senderVia.port != srcPort:
                 senderVia.rport = srcPort
@@ -709,7 +724,7 @@
 
     def handle_response(self, message, addr):
         """Override to define behavior for responses received.
-        
+
         @type message: C{Message}
         @type addr: C{tuple}
         """
@@ -760,11 +775,11 @@
 
 class Proxy(Base):
     """SIP proxy."""
-    
+
     PORT = PORT
 
     locator = None # object implementing ILocator
-    
+
     def __init__(self, host=None, port=PORT):
         """Create new instance.
 
@@ -774,7 +789,7 @@
         self.host = host or socket.getfqdn()
         self.port = port
         Base.__init__(self)
-        
+
     def getVia(self):
         """Return value of Via header for this proxy."""
         return Via(host=self.host, port=self.port)
@@ -797,10 +812,10 @@
                 d.addErrback(lambda e:
                     self.deliverResponse(self.responseFromRequest(e.code, message))
                 )
-        
+
     def handle_request_default(self, message, (srcHost, srcPort)):
         """Default request handler.
-        
+
         Default behaviour for OPTIONS and unknown methods for proxies
         is to forward message on to the client.
 
@@ -808,9 +823,9 @@
         everything.
         """
         def _mungContactHeader(uri, message):
-            message.headers['contact'][0] = uri.toString()            
+            message.headers['contact'][0] = uri.toString()
             return self.sendMessage(uri, message)
-        
+
         viaHeader = self.getVia()
         if viaHeader.toString() in message.headers["via"]:
             # must be a loop, so drop message
@@ -824,12 +839,12 @@
         d = self.locator.getAddress(uri)
         d.addCallback(self.sendMessage, message)
         d.addErrback(self._cantForwardRequest, message)
-    
+
     def _cantForwardRequest(self, error, message):
         error.trap(LookupError)
         del message.headers["via"][0] # this'll be us
         self.deliverResponse(self.responseFromRequest(404, message))
-    
+
     def deliverResponse(self, responseMessage):
         """Deliver response.
 
@@ -838,7 +853,7 @@
         # XXX we don't do multicast yet
         host = destVia.received or destVia.host
         port = destVia.rport or destVia.port or self.PORT
-        
+
         destAddr = URL(host=host, port=port)
         self.sendMessage(destAddr, responseMessage)
 
@@ -848,7 +863,7 @@
         for name in ("via", "to", "from", "call-id", "cseq"):
             response.headers[name] = request.headers.get(name, [])[:]
         return response
-    
+
     def handle_response(self, message, addr):
         """Default response handler."""
         v = parseViaHeader(message.headers["via"][0])
@@ -864,7 +879,7 @@
             self.gotResponse(message, addr)
             return
         self.deliverResponse(message)
-    
+
     def gotResponse(self, message, addr):
         """Called with responses that are addressed at this server."""
         pass
@@ -872,31 +887,31 @@
 class IAuthorizer(Interface):
     def getChallenge(peer):
         """Generate a challenge the client may respond to.
-        
+
         @type peer: C{tuple}
         @param peer: The client's address
-        
+
         @rtype: C{str}
         @return: The challenge string
         """
-    
+
     def decode(response):
         """Create a credentials object from the given response.
-        
+
         @type response: C{str}
         """
- 
+
 class BasicAuthorizer:
     """Authorizer for insecure Basic (base64-encoded plaintext) authentication.
-    
+
     This form of authentication is broken and insecure.  Do not use it.
     """
 
     implements(IAuthorizer)
-    
+
     def getChallenge(self, peer):
         return None
-    
+
     def decode(self, response):
         # At least one SIP client improperly pads its Base64 encoded messages
         for i in range(3):
@@ -917,12 +932,12 @@
 
 class DigestedCredentials(cred.credentials.UsernameHashedPassword):
     """Yet Another Simple Digest-MD5 authentication scheme"""
-    
+
     def __init__(self, username, fields, challenges):
         self.username = username
         self.fields = fields
         self.challenges = challenges
-    
+
     def checkPassword(self, password):
         method = 'REGISTER'
         response = self.fields.get('response')
@@ -937,7 +952,7 @@
         if opaque not in self.challenges:
             return False
         del self.challenges[opaque]
-        
+
         user, domain = self.username.split('@', 1)
         if uri is None:
             uri = 'sip:' + domain
@@ -946,17 +961,17 @@
             DigestCalcHA1(algo, user, domain, password, nonce, cnonce),
             nonce, nc, cnonce, qop, method, uri, None,
         )
-        
+
         return expected == response
 
 class DigestAuthorizer:
     CHALLENGE_LIFETIME = 15
-    
+
     implements(IAuthorizer)
-    
+
     def __init__(self):
         self.outstanding = {}
-    
+
     def generateNonce(self):
         c = tuple([random.randrange(sys.maxint) for _ in range(3)])
         c = '%d%d%d' % c
@@ -975,7 +990,7 @@
             'qop-options="auth"',
             'algorithm="MD5"',
         ))
-        
+
     def decode(self, response):
         response = ' '.join(response.splitlines())
         parts = response.split(',')
@@ -1003,11 +1018,11 @@
     authorizers = {
         'digest': DigestAuthorizer(),
     }
-    
+
     def __init__(self, *args, **kw):
         Proxy.__init__(self, *args, **kw)
         self.liveChallenges = {}
-        
+
     def handle_ACK_request(self, message, (host, port)):
         # XXX
         # ACKs are a client's way of indicating they got the last message
@@ -1042,7 +1057,7 @@
             m.headers.setdefault('www-authenticate', []).append(value)
         self.deliverResponse(m)
 
- 
+
     def login(self, message, host, port):
         parts = message.headers['authorization'][0].split(None, 1)
         a = self.authorizers.get(parts[0].lower())
@@ -1067,7 +1082,7 @@
     def _cbLogin(self, (i, a, l), message, host, port):
         # It's stateless, matey.  What a joke.
         self.register(message, host, port)
-    
+
     def _ebLogin(self, failure, message, host, port):
         failure.trap(cred.error.UnauthorizedLogin)
         self.unauthorized(message, host, port)
@@ -1137,7 +1152,7 @@
     """A simplistic registry for a specific domain."""
 
     implements(IRegistry, ILocator)
-    
+
     def __init__(self, domain):
         self.domain = domain # the domain we handle registration for
         self.users = {} # map username to (IDelayedCall for expiry, address URI)
@@ -1150,7 +1165,7 @@
             return defer.succeed(url)
         else:
             return defer.fail(LookupError("no such user"))
-            
+
     def getRegistrationInfo(self, userURI):
         if userURI.host != self.domain:
             return defer.fail(LookupError("unknown domain"))
@@ -1159,7 +1174,7 @@
             return defer.succeed(Registration(int(dc.getTime() - time.time()), url))
         else:
             return defer.fail(LookupError("no such user"))
-        
+
     def _expireRegistration(self, username):
         try:
             dc, url = self.users[username]
@@ -1169,7 +1184,7 @@
             dc.cancel()
             del self.users[username]
         return defer.succeed(Registration(0, url))
-    
+
     def registerAddress(self, domainURL, logicalURL, physicalURL):
         if domainURL.host != self.domain:
             log.msg("Registration for domain we don't handle.")
=== twisted/test/test_sip.py
==================================================================
--- twisted/test/test_sip.py	(revision 18594)
+++ twisted/test/test_sip.py	(patch sip-fixes level 3)
@@ -85,6 +85,25 @@
 
 """.replace("\n", "\r\n")
 
+# multiline headers (example from RFC 3261)
+response_multiline = """\
+SIP/2.0 200 OK
+Via: SIP/2.0/UDP server10.biloxi.com
+    ;branch=z9hG4bKnashds8;received=192.0.2.3
+Via: SIP/2.0/UDP bigbox3.site3.atlanta.com
+    ;branch=z9hG4bK77ef4c2312983.1;received=192.0.2.2
+Via: SIP/2.0/UDP pc33.atlanta.com
+    ;branch=z9hG4bK776asdhds ;received=192.0.2.1
+To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
+From: Alice <sip:alice@atlanta.com>;tag=1928301774
+Call-ID: a84b4c76e66710@pc33.atlanta.com
+CSeq: 314159 INVITE
+Contact: <sip:bob@192.0.2.4>
+Content-Type: application/sdp
+Content-Length: 0
+\n""".replace("\n", "\r\n")
+
+
 class TestRealm:
     def requestAvatar(self, avatarId, mind, *interfaces):
         return sip.IContact, None, lambda: None
@@ -155,7 +174,7 @@
         self.validateMessage(l[0], "INVITE", "sip:foo",
                              {"from": ["mo"], "to": ["joe"], "content-length": ["4"]},
                              "abcd")
-        
+
     def testSimpleResponse(self):
         l = self.l
         self.feedMessage(response1)
@@ -167,7 +186,23 @@
         self.assertEquals(m.body, "")
         self.assertEquals(m.finished, 1)
 
+    def testIncomplete(self):
+        # test for "aborted" request (body shorter than content-length)
+        l = self.l
+        self.feedMessage(request4[:-1])
+        self.assertEquals(len(l), 2)
 
+    def testMultiLine(self):
+        l = self.l
+        self.feedMessage(response_multiline)
+        self.assertEquals(len(l), 1)
+        m = l[0]
+        self.assertEquals(m.headers['via'][0],
+            "SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bKnashds8;received=192.0.2.3")
+        self.assertEquals(m.headers['via'][2],
+            "SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds ;received=192.0.2.1")
+
+
 class MessageParsingTestCase2(MessageParsingTestCase):
     """Same as base class, but feed data char by char."""
 
@@ -208,7 +243,7 @@
         self.assertEquals(v1.transport, v2.transport)
         self.assertEquals(v1.host, v2.host)
         self.assertEquals(v1.port, v2.port)
-    
+
     def testComplex(self):
         s = "SIP/2.0/UDP first.example.com:4000;ttl=16;maddr=224.2.0.1 ;branch=a7c6a8dlze (Example)"
         v = sip.parseViaHeader(s)
@@ -222,7 +257,7 @@
         self.assertEquals(v.toString(),
                           "SIP/2.0/UDP first.example.com:4000;ttl=16;branch=a7c6a8dlze;maddr=224.2.0.1")
         self.checkRoundtrip(v)
-    
+
     def testSimple(self):
         s = "SIP/2.0/UDP example.com;hidden"
         v = sip.parseViaHeader(s)
@@ -236,7 +271,13 @@
         self.assertEquals(v.toString(),
                           "SIP/2.0/UDP example.com:5060;hidden")
         self.checkRoundtrip(v)
-    
+
+    def testOneIsNotTrue(self):
+        s = "SIP/2.0/UDP example.com;ttl=1"
+        v = sip.parseViaHeader(s)
+        self.assertEquals(v.toString().rsplit(';')[1], "ttl=1")
+        self.checkRoundtrip(v)
+
     def testSimpler(self):
         v = sip.Via("example.com")
         self.checkRoundtrip(v)
@@ -253,9 +294,14 @@
         self.assertEquals(v.port, 5060)
         self.assertEquals(v.received, "22.13.1.5")
         self.assertEquals(v.rport, 12345)
-        
+
         self.assertNotEquals(v.toString().find("rport=12345"), -1)
 
+    def testUnknownParam(self):
+        s = "SIP/2.0/UDP example.com;alias;eggs;spam=1234"
+        v = sip.parseViaHeader(s)
+        self.checkRoundtrip(v)
+
 class URLTestCase(unittest.TestCase):
 
     def testRoundtrip(self):
@@ -305,8 +351,8 @@
     implements(sip.ILocator)
     def getAddress(self, logicalURL):
         return defer.fail(LookupError())
-    
 
+
 class ProxyTestCase(unittest.TestCase):
 
     def setUp(self):
@@ -314,7 +360,7 @@
         self.proxy.locator = DummyLocator()
         self.sent = []
         self.proxy.sendMessage = lambda dest, msg: self.sent.append((dest, msg))
-    
+
     def testRequestForward(self):
         r = sip.Request("INVITE", "sip:foo")
         r.addHeader("via", sip.Via("1.2.3.4").toString())
@@ -334,7 +380,7 @@
                            "SIP/2.0/UDP 1.2.3.4:5060",
                            "SIP/2.0/UDP 1.2.3.5:5060"])
 
-    
+
     def testReceivedRequestForward(self):
         r = sip.Request("INVITE", "sip:foo")
         r.addHeader("via", sip.Via("1.2.3.4").toString())
@@ -346,15 +392,15 @@
         self.assertEquals(m.headers["via"],
                           ["SIP/2.0/UDP 127.0.0.1:5060",
                            "SIP/2.0/UDP 1.2.3.4:5060;received=1.1.1.1"])
-        
 
+
     def testResponseWrongVia(self):
         # first via must match proxy's address
         r = sip.Response(200)
         r.addHeader("via", sip.Via("foo.com").toString())
         self.proxy.datagramReceived(r.toString(), ("1.1.1.1", 5060))
         self.assertEquals(len(self.sent), 0)
-    
+
     def testResponseForward(self):
         r = sip.Response(200)
         r.addHeader("via", sip.Via("127.0.0.1").toString())
@@ -365,7 +411,7 @@
         self.assertEquals((dest.host, dest.port), ("client.com", 1234))
         self.assertEquals(m.code, 200)
         self.assertEquals(m.headers["via"], ["SIP/2.0/UDP client.com:1234"])
-        
+
     def testReceivedResponseForward(self):
         r = sip.Response(200)
         r.addHeader("via", sip.Via("127.0.0.1").toString())
@@ -374,7 +420,7 @@
         self.assertEquals(len(self.sent), 1)
         dest, m = self.sent[0]
         self.assertEquals((dest.host, dest.port), ("client.com", 5060))
-        
+
     def testResponseToUs(self):
         r = sip.Response(200)
         r.addHeader("via", sip.Via("127.0.0.1").toString())
@@ -385,10 +431,10 @@
         m, addr = l[0]
         self.assertEquals(len(m.headers.get("via", [])), 0)
         self.assertEquals(m.code, 200)
-    
+
     def testLoop(self):
         r = sip.Request("INVITE", "sip:foo")
-        r.addHeader("via", sip.Via("1.2.3.4").toString()) 
+        r.addHeader("via", sip.Via("1.2.3.4").toString())
         r.addHeader("via", sip.Via("127.0.0.1").toString())
         self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
         self.assertEquals(self.sent, [])
@@ -431,7 +477,7 @@
         r.addHeader("contact", "sip:joe@client.com:1234")
         r.addHeader("via", sip.Via("client.com").toString())
         self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
-    
+
     def unregister(self):
         r = sip.Request("REGISTER", "sip:bell.example.com")
         r.addHeader("to", "sip:joe@bell.example.com")
@@ -439,7 +485,7 @@
         r.addHeader("via", sip.Via("client.com").toString())
         r.addHeader("expires", "0")
         self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
-    
+
     def testRegister(self):
         self.register()
         dest, m = self.sent[0]
@@ -482,7 +528,7 @@
     def testFailedAuthentication(self):
         self.addPortal()
         self.register()
-        
+
         self.assertEquals(len(self.registry.users), 0)
         self.assertEquals(len(self.sent), 1)
         dest, m = self.sent[0]
@@ -500,13 +546,13 @@
         r.addHeader("via", sip.Via("client.com").toString())
         r.addHeader("authorization", "Basic " + "userXname:passXword".encode('base64'))
         self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
-        
+
         self.assertEquals(len(self.registry.users), 1)
         self.assertEquals(len(self.sent), 1)
         dest, m = self.sent[0]
         self.assertEquals(m.code, 200)
 
-    
+
     def testFailedBasicAuthentication(self):
         self.addPortal()
         self.proxy.authorizers = self.proxy.authorizers.copy()
@@ -518,7 +564,7 @@
         r.addHeader("via", sip.Via("client.com").toString())
         r.addHeader("authorization", "Basic " + "userXname:password".encode('base64'))
         self.proxy.datagramReceived(r.toString(), ("client.com", 5060))
-        
+
         self.assertEquals(len(self.registry.users), 0)
         self.assertEquals(len(self.sent), 1)
         dest, m = self.sent[0]
@@ -546,7 +592,7 @@
         d = self.proxy.locator.getAddress(url)
         self.assertFailure(d, LookupError)
         return d
-    
+
     def testNoContactLookup(self):
         self.register()
         url = sip.URL(username="jane", host="bell.example.com")
@@ -623,8 +669,8 @@
             self.assertEquals(r.code, 200)
         d.addCallback(check)
         return d
-        
 
+
 registerRequest = """
 REGISTER sip:intarweb.us SIP/2.0\r
 Via: SIP/2.0/UDP 192.168.1.100:50609\r
@@ -724,7 +770,7 @@
         for d, uri in self.registry.users.values():
             d.cancel()
         del self.proxy
-    
+
     def testChallenge(self):
         self.proxy.datagramReceived(registerRequest, ("127.0.0.1", 5632))
         self.assertEquals(
