Index: twisted/protocols/sip.py
===================================================================
--- twisted/protocols/sip.py	(revision 18550)
+++ twisted/protocols/sip.py	(working copy)
@@ -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(":")
@@ -266,11 +266,11 @@
         self.tag = tag
         self.ttl = ttl
         self.maddr = maddr
-        if other == None:
+        if other is None:
             self.other = []
         else:
             self.other = other
-        if headers == None:
+        if headers is None:
             self.headers = {}
         else:
             self.headers = headers
@@ -301,7 +301,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 +419,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 +434,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 +465,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())
 
@@ -479,7 +479,7 @@
     def __init__(self, code, phrase=None, version="SIP/2.0"):
         Message.__init__(self)
         self.code = code
-        if phrase == None:
+        if phrase is None:
             phrase = statusCodes[code]
         self.phrase = phrase
 
@@ -501,9 +501,9 @@
     acceptResponses = 1
     acceptRequests = 1
     state = "firstline" # or "headers", "body" or "invalid"
-    
+
     debug = 0
-    
+
     def __init__(self, messageReceivedCallback):
         self.messageReceived = messageReceivedCallback
         self.reset()
@@ -514,11 +514,11 @@
         self.bodyReceived = 0 # how much of the body we received
         self.message = 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()
@@ -527,30 +527,30 @@
         if self.state != "body":
             self.reset()
             return
-        if self.length == None:
+        if self.length is 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"):
@@ -608,12 +608,12 @@
         self.message.creationFinished()
         self.messageReceived(self.message)
         self.reset(remainingData)
-    
+
     def rawDataReceived(self, data):
         assert self.state in ("body", "invalid")
         if self.state == "invalid":
             return
-        if self.length == None:
+        if self.length is None:
             self.message.bodyDataReceived(data)
         else:
             dataLen = len(data)
@@ -631,10 +631,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 +658,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 +709,7 @@
 
     def handle_response(self, message, addr):
         """Override to define behavior for responses received.
-        
+
         @type message: C{Message}
         @type addr: C{tuple}
         """
@@ -760,11 +760,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 +774,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 +797,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 +808,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 +824,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 +838,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 +848,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 +864,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 +872,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 +917,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 +937,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 +946,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 +975,7 @@
             'qop-options="auth"',
             'algorithm="MD5"',
         ))
-        
+
     def decode(self, response):
         response = ' '.join(response.splitlines())
         parts = response.split(',')
@@ -1003,11 +1003,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 +1042,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 +1067,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 +1137,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 +1150,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 +1159,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 +1169,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.")
Index: twisted/test/test_sip.py
===================================================================
--- twisted/test/test_sip.py	(revision 18550)
+++ twisted/test/test_sip.py	(working copy)
@@ -155,7 +155,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 +167,13 @@
         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)
 
+
 class MessageParsingTestCase2(MessageParsingTestCase):
     """Same as base class, but feed data char by char."""
 
@@ -208,7 +214,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 +228,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 +242,7 @@
         self.assertEquals(v.toString(),
                           "SIP/2.0/UDP example.com:5060;hidden")
         self.checkRoundtrip(v)
-    
+
     def testSimpler(self):
         v = sip.Via("example.com")
         self.checkRoundtrip(v)
@@ -253,7 +259,7 @@
         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)
 
 class URLTestCase(unittest.TestCase):
@@ -305,8 +311,8 @@
     implements(sip.ILocator)
     def getAddress(self, logicalURL):
         return defer.fail(LookupError())
-    
 
+
 class ProxyTestCase(unittest.TestCase):
 
     def setUp(self):
@@ -314,7 +320,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 +340,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 +352,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 +371,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 +380,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 +391,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 +437,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 +445,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 +488,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 +506,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 +524,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 +552,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 +629,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 +730,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(
