~divmod-dev/divmod.org/dangling-1091

« back to all changes in this revision

Viewing changes to Sign/sign/sip.py

  • Committer: glyph
  • Date: 2005-10-29 23:28:40 UTC
  • Revision ID: svn-v4:866e43f7-fbfc-0310-8f2a-ec88d1da2979:trunk:2703
dash: I hope this doesn't screw anything up in your local checkout, but that name doesn't make any sense: Sine, like a Sine wave, related to sound...

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2005 Divmod, Inc.  See LICENSE file for details
2
 
# -*- test-case-name: mantissa.test.test_sip -*-
3
 
 
4
 
# "I have always wished that my computer would be as easy to use as my
5
 
# telephone. My wish has come true. I no longer know how to use my
6
 
# telephone." - Bjarne Stroustrup
7
 
 
8
 
import socket, random, md5, sys, urllib
9
 
 
10
 
from twisted.python import log, util
11
 
from twisted.internet import protocol, defer, reactor, abstract
12
 
from twisted.names import client
13
 
 
14
 
from twisted.cred import credentials
15
 
from twisted.cred.error import UnauthorizedLogin
16
 
from twisted.protocols import basic
17
 
 
18
 
from zope.interface import  Interface, implements
19
 
 
20
 
from axiom.userbase  import Preauthenticated
21
 
from axiom.errors import NoSuchUser
22
 
 
23
 
from epsilon.modal import mode, ModalType
24
 
 
25
 
debuggingEnabled=0
26
 
 
27
 
def debug(txt):
28
 
    if debuggingEnabled:
29
 
        print (txt)
30
 
 
31
 
VIA_COOKIE = "z9hG4bK"
32
 
PORT = 5060
33
 
DEFAULT_REGISTRATION_LIFETIME = 3600
34
 
 
35
 
# SIP headers have short forms
36
 
shortHeaders = {"call-id": "i",
37
 
                "contact": "m",
38
 
                "content-encoding": "e",
39
 
                "content-length": "l",
40
 
                "content-type": "c",
41
 
                "from": "f",
42
 
                "subject": "s",
43
 
                "to": "t",
44
 
                "via": "v",
45
 
                }
46
 
 
47
 
longHeaders = {}
48
 
for k, v in shortHeaders.items():
49
 
    longHeaders[v] = k
50
 
del k, v
51
 
 
52
 
statusCodes = {
53
 
    100: "Trying",
54
 
    180: "Ringing",
55
 
    181: "Call Is Being Forwarded",
56
 
    182: "Queued",
57
 
 
58
 
    200: "OK",
59
 
 
60
 
    300: "Multiple Choices",
61
 
    301: "Moved Permanently",
62
 
    302: "Moved Temporarily",
63
 
    303: "See Other",
64
 
    305: "Use Proxy",
65
 
    380: "Alternative Service",
66
 
 
67
 
    400: "Bad Request",
68
 
    401: "Unauthorized",
69
 
    402: "Payment Required",
70
 
    403: "Forbidden",
71
 
    404: "Not Found",
72
 
    406: "Not Acceptable",
73
 
    407: "Proxy Authentication Required",
74
 
    408: "Request Timeout",
75
 
    409: "Conflict",
76
 
    410: "Gone",
77
 
    411: "Length Required",
78
 
    413: "Request Entity Too Large",
79
 
    414: "Request-URI Too Large",
80
 
    415: "Unsupported Media Type",
81
 
    420: "Bad Extension",
82
 
    480: "Temporarily not available",
83
 
    481: "Call Leg/Transaction Does Not Exist",
84
 
    482: "Loop Detected",
85
 
    483: "Too Many Hops",
86
 
    484: "Address Incomplete",
87
 
    485: "Ambiguous",
88
 
    486: "Busy Here",
89
 
    487: "Request Cancelled",
90
 
 
91
 
    500: "Internal Server Error",
92
 
    501: "Not Implemented",
93
 
    502: "Bad Gateway",
94
 
    503: "Service Unavailable",
95
 
    504: "Gateway Time-out",
96
 
    505: "SIP Version not supported",
97
 
 
98
 
    600: "Busy Everywhere",
99
 
    603: "Decline",
100
 
    604: "Does not exist anywhere",
101
 
    606: "Not Acceptable",
102
 
}
103
 
# some headers change case strangely.
104
 
specialCases = {
105
 
    'cseq': 'CSeq',
106
 
    'call-id': 'Call-ID',
107
 
    'www-authenticate': 'WWW-Authenticate',
108
 
    'proxy-authenticate': 'Proxy-Authenticate',
109
 
    'proxy-authorization':'Proxy-Authorization',
110
 
    'record-route':'Record-Route',
111
 
}
112
 
 
113
 
 
114
 
 
115
 
def DigestCalcHA1(
116
 
    pszAlg,
117
 
    pszUserName,
118
 
    pszRealm,
119
 
    pszPassword,
120
 
    pszNonce,
121
 
    pszCNonce,
122
 
):
123
 
    m = md5.md5()
124
 
    m.update(pszUserName)
125
 
    m.update(":")
126
 
    m.update(pszRealm)
127
 
    m.update(":")
128
 
    m.update(pszPassword)
129
 
    HA1 = m.digest()
130
 
    if pszAlg == "md5-sess":
131
 
        m = md5.md5()
132
 
        m.update(HA1)
133
 
        m.update(":")
134
 
        m.update(pszNonce)
135
 
        m.update(":")
136
 
        m.update(pszCNonce)
137
 
        HA1 = m.digest()
138
 
    return HA1.encode('hex')
139
 
 
140
 
def DigestCalcResponse(
141
 
    HA1,
142
 
    pszNonce,
143
 
    pszNonceCount,
144
 
    pszCNonce,
145
 
    pszQop,
146
 
    pszMethod,
147
 
    pszDigestUri,
148
 
    pszHEntity,
149
 
):
150
 
    m = md5.md5()
151
 
    m.update(pszMethod)
152
 
    m.update(":")
153
 
    m.update(pszDigestUri)
154
 
    if pszQop == "auth-int":
155
 
        m.update(":")
156
 
        m.update(pszHEntity)
157
 
    HA2 = m.digest().encode('hex')
158
 
 
159
 
    m = md5.md5()
160
 
    m.update(HA1)
161
 
    m.update(":")
162
 
    m.update(pszNonce)
163
 
    m.update(":")
164
 
    if pszNonceCount and pszCNonce: # pszQop:
165
 
        m.update(pszNonceCount)
166
 
        m.update(":")
167
 
        m.update(pszCNonce)
168
 
        m.update(":")
169
 
        m.update(pszQop)
170
 
        m.update(":")
171
 
    m.update(HA2)
172
 
    hash = m.digest().encode('hex')
173
 
    return hash
174
 
 
175
 
class Via:
176
 
    """A SIP Via header."""
177
 
 
178
 
    def __init__(self, host, port=PORT, transport="UDP", ttl=None, hidden=False,
179
 
                 received=None, rport=None, branch=None, maddr=None):
180
 
        self.transport = transport
181
 
        self.host = host
182
 
        self.port = port
183
 
        self.ttl = ttl
184
 
        self.hidden = hidden
185
 
        self.received = received
186
 
        self.rport = rport
187
 
        self.branch = branch
188
 
        self.maddr = maddr
189
 
 
190
 
    def toString(self):
191
 
        s = "SIP/2.0/%s %s:%s" % (self.transport, self.host, self.port)
192
 
        if self.hidden:
193
 
            s += ";hidden"
194
 
        for n in "ttl", "branch", "maddr", "received", "rport":
195
 
            value = getattr(self, n)
196
 
            if value == True:
197
 
                s += ";" + n
198
 
            elif value != None:
199
 
                s += ";%s=%s" % (n, value)
200
 
        return s
201
 
 
202
 
 
203
 
def parseViaHeader(value):
204
 
    """Parse a Via header, returning Via class instance."""
205
 
    try:
206
 
        parts = value.split(";")
207
 
        sent, params = parts[0], parts[1:]
208
 
        protocolinfo, by = sent.split(" ", 1)
209
 
        result = {}
210
 
        pname, pversion, transport = protocolinfo.split("/")
211
 
        if pname != "SIP" or pversion != "2.0":
212
 
            raise SIPError(400, "wrong protocol or version: %r" % value)
213
 
        result["transport"] = transport
214
 
        if ":" in by:
215
 
            host, port = by.split(":")
216
 
            result["port"] = int(port)
217
 
            result["host"] = host
218
 
        else:
219
 
            result["host"] = by
220
 
        for p in params:
221
 
            # it's the comment-striping dance!
222
 
            p = p.strip().split(" ", 1)
223
 
            if len(p) == 1:
224
 
                p, comment = p[0], ""
225
 
            else:
226
 
                p, comment = p
227
 
            if p == "hidden":
228
 
                result["hidden"] = True
229
 
                continue
230
 
            parts = p.split("=", 1)
231
 
            if len(parts) == 1:
232
 
                name, value = parts[0], True
233
 
            else:
234
 
                name, value = parts
235
 
                if name in ("rport", "ttl"):
236
 
                    value = int(value)
237
 
            result[name] = value
238
 
        return Via(**result)
239
 
    except:
240
 
        raise SIPError(400)
241
 
 
242
 
class URL:
243
 
    """A SIP URL."""
244
 
 
245
 
    def __init__(self, host, username=None, password=None, port=None,
246
 
                 transport=None, usertype=None, method=None,
247
 
                 ttl=None, maddr=None, tag=None, other=None, headers=None):
248
 
        self.username = username
249
 
        self.host = host
250
 
        self.password = password
251
 
        self.port = port
252
 
        self.transport = transport
253
 
        self.usertype = usertype
254
 
        self.method = method
255
 
        self.tag = tag
256
 
        self.ttl = ttl
257
 
        self.maddr = maddr
258
 
        if other == None:
259
 
            self.other = {}
260
 
        else:
261
 
            self.other = other
262
 
        if headers == None:
263
 
            self.headers = {}
264
 
        else:
265
 
            self.headers = headers
266
 
 
267
 
    def toCredString(self):
268
 
        return '%s@%s' % (self.username, self.host)
269
 
 
270
 
    def toString(self):
271
 
        l = []; w = l.append
272
 
        w("sip:")
273
 
        if self.username != None:
274
 
            w(urllib.quote(self.username))
275
 
            if self.password != None:
276
 
                w(":%s" % (urllib.quote(self.password)))
277
 
            w("@")
278
 
        w(self.host)
279
 
        if self.port != None:
280
 
            w(":%d" % self.port)
281
 
        if self.usertype != None:
282
 
            w(";user=%s" % self.usertype)
283
 
        for n in ("transport", "ttl", "maddr", "method", "tag"):
284
 
            v = getattr(self, n)
285
 
            if v != None:
286
 
                w(";%s=%s" % (urllib.quote(n), urllib.quote(v)))
287
 
        for k, v in self.other.iteritems():
288
 
            if v:
289
 
                w(";%s=%s" % (urllib.quote(k), urllib.quote(v)))
290
 
            else:
291
 
                w(";%s" % k)
292
 
        if self.headers:
293
 
            w("?")
294
 
            w("&".join([("%s=%s" % (specialCases.get(h) or urllib.quote(h).capitalize(), urllib.quote(v))) for (h, v) in self.headers.items()]))
295
 
        return "".join(l)
296
 
 
297
 
    def __str__(self):
298
 
        return self.toString()
299
 
 
300
 
    def __repr__(self):
301
 
        return '<sip.URL %s>' % self.toString()
302
 
 
303
 
    def __cmp__(self, other):
304
 
        return cmp(self.__dict__, other.__dict__)
305
 
 
306
 
    def __hash__(self):
307
 
        #I could include the other stuff but what's the point?
308
 
        #this is the most usual stuff and python is very kind to collisions
309
 
        return hash((self.host, self.username, self.port, tuple(self.headers.items())))
310
 
 
311
 
def parseURL(url, host=None, port=None):
312
 
    """Return string into URL object.
313
 
 
314
 
    URIs are of of form 'sip:user@example.com'.
315
 
    """
316
 
    d = {}
317
 
    if not url.startswith("sip:"):
318
 
        raise SIPError(416, "Unsupported URI scheme: " + url[:4])
319
 
    parts = url[4:].split(";")
320
 
    userdomain, params = parts[0], parts[1:]
321
 
    udparts = userdomain.split("@", 1)
322
 
    if len(udparts) == 2:
323
 
        userpass, hostport = udparts
324
 
        upparts = userpass.split(":", 1)
325
 
        if len(upparts) == 1:
326
 
            d["username"] = urllib.unquote(upparts[0])
327
 
        else:
328
 
            d["username"] = urllib.unquote(upparts[0])
329
 
            d["password"] = urllib.unquote(upparts[1])
330
 
    else:
331
 
        hostport = udparts[0]
332
 
    hpparts = hostport.split(":", 1)
333
 
    if len(hpparts) == 1:
334
 
        d["host"] = hpparts[0]
335
 
    else:
336
 
        d["host"] = hpparts[0]
337
 
        d["port"] = int(hpparts[1])
338
 
    if host != None:
339
 
        d["host"] = host
340
 
    if port != None:
341
 
        d["port"] = port
342
 
    for p in params:
343
 
        if p == params[-1] and "?" in p:
344
 
            d["headers"] = h = {}
345
 
            p, headers = p.split("?", 1)
346
 
            for header in headers.split("&"):
347
 
                k, v = header.split("=")
348
 
                h[urllib.unquote(k)] = urllib.unquote(v)
349
 
        nv = p.split("=", 1)
350
 
        if len(nv) == 1:
351
 
            d.setdefault("other", {})[urllib.unquote(p)] = ''
352
 
            continue
353
 
        name, value = map(urllib.unquote, nv)
354
 
        if name == "user":
355
 
            d["usertype"] = value
356
 
        elif name in ("transport", "ttl", "maddr", "method", "tag"):
357
 
            if name == "ttl":
358
 
                value = int(value)
359
 
            d[name] = value
360
 
        else:
361
 
            d.setdefault("other", {})[name] = value
362
 
    return URL(**d)
363
 
 
364
 
 
365
 
def cleanRequestURL(url):
366
 
    """Clean a URL from a Request line."""
367
 
    url.transport = None
368
 
    url.maddr = None
369
 
    url.ttl = None
370
 
    url.headers = {}
371
 
 
372
 
 
373
 
def parseAddress(address, host=None, port=None, clean=0):
374
 
    """Return (name, uri, params) for From/To/Contact header.
375
 
 
376
 
    @param clean: remove unnecessary info, usually for From and To headers.
377
 
 
378
 
    Although many headers such as From can contain any valid URI, even those
379
 
    with schemes other than 'sip', this function raises SIPError if the scheme
380
 
    is not 'sip' because the upper layers do not support it.
381
 
    """
382
 
    def splitParams(paramstring):
383
 
        params = {}
384
 
        paramstring = paramstring.strip()
385
 
        if paramstring:
386
 
            for l in paramstring.split(";"):
387
 
                if not l:
388
 
                    continue
389
 
                x = l.split("=")
390
 
                if len(x) > 1:
391
 
                    params[x[0]] = x[1]
392
 
                else:
393
 
                    params [x[0]] = ''
394
 
        return params
395
 
    try:
396
 
        address = address.strip()
397
 
        # simple 'sip:foo' case
398
 
        if not '<' in address:
399
 
            i = address.rfind(";tag=")
400
 
            if i > -1:
401
 
 
402
 
                params = splitParams(address[i:])
403
 
                address = address[:i]
404
 
            else:
405
 
                params = {}
406
 
            return "", parseURL(address, host=host, port=port), params
407
 
        params = {}
408
 
        name, url = address.split("<", 1)
409
 
        name = name.strip()
410
 
        if name.startswith('"'):
411
 
            name = name[1:]
412
 
        if name.endswith('"'):
413
 
            name = name[:-1]
414
 
        import re
415
 
        name = re.sub(r'\\(.)', r'\1', name)
416
 
        url, paramstring = url.split(">", 1)
417
 
        url = parseURL(url, host=host, port=port)
418
 
        params = splitParams(paramstring)
419
 
        if clean:
420
 
            # rfc 2543 6.21
421
 
            url.ttl = None
422
 
            url.headers = {}
423
 
            url.transport = None
424
 
            url.maddr = None
425
 
        return name.decode('utf8','replace'), url, params
426
 
    except:
427
 
        log.err()
428
 
        raise SIPError(400)
429
 
    
430
 
class Message:
431
 
    """A SIP message."""
432
 
 
433
 
    length = None
434
 
 
435
 
    def __init__(self, version):
436
 
        self.headers = util.OrderedDict() # map name to list of values
437
 
        self.body = ""
438
 
        self.finished = 0
439
 
        self.version = version
440
 
 
441
 
    def copy(self):
442
 
        c = Message(self.version)
443
 
        c.headers = self.headers.copy()
444
 
        c.body = self.body
445
 
        c.finished = self.finished
446
 
        return c
447
 
 
448
 
    def __eq__(self, other):
449
 
        return (other.__class__ == self.__class__
450
 
                and self.version == other.version
451
 
                and dict([(k,v) for k,v in self.headers.items() if v]) == dict([(k,v) for k,v in other.headers.items() if v])
452
 
                and self.body == other.body)
453
 
    
454
 
    def addHeader(self, name, value):
455
 
        name = name.lower()
456
 
        name = longHeaders.get(name, name)
457
 
        if name == "content-length":
458
 
            self.length = int(value)
459
 
        self.headers.setdefault(name,[]).append(value)
460
 
 
461
 
    def bodyDataReceived(self, data):
462
 
        self.body += data
463
 
 
464
 
    def creationFinished(self):
465
 
        if (self.length != None) and (self.length != len(self.body)):
466
 
            raise ValueError, "wrong body length"
467
 
        self.finished = 1
468
 
 
469
 
    def toString(self):
470
 
        s = "%s\r\n" % self._getHeaderLine()
471
 
        for n, vs in self.headers.items():
472
 
            for v in vs:
473
 
                s += "%s: %s\r\n" % (specialCases.get(n) or n.capitalize(), v)
474
 
        s += "\r\n"
475
 
        s += self.body
476
 
        return s
477
 
 
478
 
    def _getHeaderLine(self):
479
 
        raise NotImplementedError
480
 
 
481
 
 
482
 
class Request(Message):
483
 
    """A Request for a URI"""
484
 
 
485
 
 
486
 
    def __init__(self, method, uri, version="SIP/2.0"):
487
 
        Message.__init__(self, version)
488
 
        self.method = method
489
 
        if isinstance(uri, URL):
490
 
            self.uri = uri
491
 
        else:
492
 
            self.uri = parseURL(uri)
493
 
            cleanRequestURL(self.uri)
494
 
 
495
 
    def copy(self):
496
 
        c = Message.copy(self)
497
 
        c.__class__ = Request
498
 
        c.method = self.method
499
 
        c.uri = self.uri
500
 
        return c
501
 
 
502
 
    def __eq__(self, other):
503
 
        return Message.__eq__(self, other) and self.method == other.method and self.uri == other.uri
504
 
 
505
 
    def __repr__(self):
506
 
        return "<SIP Request %d:%s %s>" % (id(self), self.method, self.uri.toString())
507
 
 
508
 
    def _getHeaderLine(self):
509
 
        return "%s %s %s" % (self.method, self.uri.toString(), self.version)
510
 
 
511
 
 
512
 
class Response(Message):
513
 
    """A Response to a URI Request"""
514
 
 
515
 
    def __init__(self, code, phrase=None, version="SIP/2.0"):
516
 
        Message.__init__(self, version)
517
 
        self.code = code
518
 
        if phrase == None:
519
 
            phrase = statusCodes[code]
520
 
        self.phrase = phrase
521
 
 
522
 
    def __eq__(self, other):
523
 
        return Message.__eq__(self, other) and self.code == other.code
524
 
 
525
 
    def __repr__(self):
526
 
        return "<SIP Response %d:%s>" % (id(self), self.code)
527
 
 
528
 
    def _getHeaderLine(self):
529
 
        return "SIP/2.0 %s %s" % (self.code, self.phrase)
530
 
 
531
 
def splitMultiHeader(s):
532
 
    "Split a header on commas, ignoring commas in quotes and escaped quotes."
533
 
    headers = []
534
 
    last = 0
535
 
    quoted = False
536
 
    for i in xrange(len(s)):
537
 
        if s[i] == '"':
538
 
            quoted = ~quoted
539
 
            if i == 0: continue
540
 
            j = i-1
541
 
            while s[j] == '\\':
542
 
                quoted = ~quoted
543
 
                j = j-1
544
 
        if not quoted and s[i] == ',':
545
 
            headers.append(s[last:i])
546
 
            last = i+1
547
 
    headers.append(s[last:])
548
 
    return headers
549
 
 
550
 
 
551
 
 
552
 
class MessagesParser(basic.LineReceiver):
553
 
    """A SIP messages parser.
554
 
 
555
 
    Expects dataReceived, dataDone repeatedly,
556
 
    in that order. Shouldn't be connected to actual transport.
557
 
    """
558
 
 
559
 
    version = "SIP/2.0"
560
 
    acceptResponses = 1
561
 
    acceptRequests = 1
562
 
    state = "firstline" # or "headers", "body" or "invalid"
563
 
    multiheaders = ['accept','accept-encoding', 'accept-language', 'alert-info', 'allow', 'authentication-info', 'call-info',  'content-encoding', 'content-language', 'error-info', 'in-reply-to', 'proxy-require',  'require',  'supported', 'unsupported', 'via', 'warning']
564
 
    multiAddressHeaders = ['route', 'record-route', 'contact']
565
 
    debug = 0
566
 
 
567
 
    def __init__(self, messageReceivedCallback):
568
 
        self.messageReceived = messageReceivedCallback
569
 
        self.reset()
570
 
 
571
 
    def reset(self, remainingData=""):
572
 
        self.state = "firstline"
573
 
        self.length = None # body length
574
 
        self.bodyReceived = 0 # how much of the body we received
575
 
        self.message = None
576
 
        self.setLineMode(remainingData)
577
 
 
578
 
    def invalidMessage(self, exc=None):
579
 
        self.dataDone()
580
 
        if isinstance(exc, SIPError):
581
 
            raise exc
582
 
        else:
583
 
            raise SIPError(400)
584
 
 
585
 
    def dataDone(self):
586
 
        # clear out any buffered data that may be hanging around
587
 
        self.clearLineBuffer()
588
 
        if self.state == "firstline":
589
 
            return
590
 
        if self.state != "body":
591
 
            self.reset()
592
 
            return
593
 
        if self.length == None:
594
 
            # no content-length header, so end of data signals message done
595
 
            self.messageDone()
596
 
        elif self.length < self.bodyReceived:
597
 
            # aborted in the middle
598
 
            self.reset()
599
 
        else:
600
 
            # we have enough data and message wasn't finished? something is wrong
601
 
            raise RuntimeError, "corrupted or overflowed SIP packet"
602
 
 
603
 
    def dataReceived(self, data):
604
 
        try:
605
 
            basic.LineReceiver.dataReceived(self, data)
606
 
        except Exception, e:
607
 
            log.err()
608
 
            self.invalidMessage(e)
609
 
 
610
 
    def handleFirstLine(self, line):
611
 
        """Expected to create self.message."""
612
 
        raise NotImplementedError
613
 
 
614
 
    def lineLengthExceeded(self, line):
615
 
        self.invalidMessage()
616
 
 
617
 
    def lineReceived(self, line):
618
 
        if self.state == "firstline":
619
 
            while line.startswith("\n") or line.startswith("\r"):
620
 
                line = line[1:]
621
 
            if not line:
622
 
                return
623
 
            try:
624
 
                a, b, c = line.split(" ", 2)
625
 
            except ValueError:
626
 
                self.invalidMessage()
627
 
                return
628
 
            if a == "SIP/2.0" and self.acceptResponses:
629
 
                # response
630
 
                try:
631
 
                    code = int(b)
632
 
                except ValueError:
633
 
                    self.invalidMessage()
634
 
                    return
635
 
                self.message = Response(code, c)
636
 
            elif c == "SIP/2.0" and self.acceptRequests:
637
 
                self.message = Request(a, b)
638
 
            else:
639
 
                self.invalidMessage()
640
 
                return
641
 
            self.state = "headers"
642
 
            self.prevline = None
643
 
            return
644
 
        else:
645
 
            assert self.state == "headers"
646
 
        if line:
647
 
            x = line.lstrip()
648
 
            if line != x:
649
 
                #leading whitespace: this is a continuation line.
650
 
                self.prevline += x
651
 
            else:
652
 
                #new header
653
 
                if self.prevline:
654
 
                    try:
655
 
                        self.processHeaderLine(self.prevline)
656
 
                    except ValueError:
657
 
                        self.invalidMessage()
658
 
                        return
659
 
                self.prevline = line
660
 
 
661
 
        else:
662
 
            # CRLF, we now have message body until self.length bytes,
663
 
            # or if no length was given, until there is no more data
664
 
            # from the connection sending us data.
665
 
            self.state = "body"
666
 
            try:
667
 
                self.processHeaderLine(self.prevline)
668
 
            except ValueError:
669
 
                self.invalidMessage()
670
 
                return
671
 
            if self.length == 0:
672
 
                self.messageDone()
673
 
                return
674
 
            self.setRawMode()
675
 
 
676
 
    def processHeaderLine(self, line):
677
 
        name, value = line.split(":", 1)
678
 
        name, value = name.rstrip().lower(), value.lstrip()
679
 
 
680
 
        if name in self.multiheaders:
681
 
            multi = value.split(',')
682
 
            if multi:
683
 
                for v in multi:
684
 
                    self.message.addHeader(name, v.strip())
685
 
            else:
686
 
                self.message.addHeader(v)
687
 
        elif name in self.multiAddressHeaders:
688
 
            for val in splitMultiHeader(value):
689
 
                self.message.addHeader(name, val)
690
 
        else:
691
 
            self.message.addHeader(name, value)
692
 
        if name.lower() == "content-length":
693
 
            self.length = int(value.lstrip())
694
 
 
695
 
    def messageDone(self, remainingData=""):
696
 
        assert self.state == "body"
697
 
        self.message.creationFinished()
698
 
        self.messageReceived(self.message)
699
 
        self.reset(remainingData)
700
 
 
701
 
    def rawDataReceived(self, data):
702
 
        if self.length == None:
703
 
            self.message.bodyDataReceived(data)
704
 
        else:
705
 
            dataLen = len(data)
706
 
            expectedLen = self.length - self.bodyReceived
707
 
            if dataLen > expectedLen:
708
 
                self.message.bodyDataReceived(data[:expectedLen])
709
 
                self.messageDone(data[expectedLen:])
710
 
                return
711
 
            else:
712
 
                self.bodyReceived += dataLen
713
 
                self.message.bodyDataReceived(data)
714
 
                if self.bodyReceived == self.length:
715
 
                    self.messageDone()
716
 
 
717
 
 
718
 
 
719
 
class SIPError(Exception):
720
 
    def __init__(self, code, phrase=None):
721
 
        if phrase is None:
722
 
            phrase = statusCodes[code]
723
 
        Exception.__init__(self, "SIP error (%d): %s" % (code, phrase))
724
 
        self.code = code
725
 
        self.phrase = phrase
726
 
 
727
 
class SIPLookupError(SIPError):
728
 
    """An error raised specifically for SIP lookup errors.
729
 
    """
730
 
    def __init__(self, code=404, phrase=None):
731
 
        SIPError.__init__(self, code=code, phrase=phrase)
732
 
 
733
 
class RegistrationError(SIPError):
734
 
    """Registration was not possible."""
735
 
 
736
 
class ISIPEvent(Interface):
737
 
    "A log message concerning SIP"
738
 
 
739
 
 
740
 
class IAuthorizer(Interface):
741
 
    def getChallenge(peer):
742
 
        """Generate a challenge the client may respond to.
743
 
 
744
 
        @type peer: C{tuple}
745
 
        @param peer: The client's address
746
 
 
747
 
        @rtype: C{str}
748
 
        @return: The challenge string
749
 
        """
750
 
 
751
 
    def decode(response):
752
 
        """Create a credentials object from the given response.
753
 
 
754
 
        @type response: C{str}
755
 
        """
756
 
 
757
 
 
758
 
 
759
 
class BasicAuthorizer:
760
 
    """Authorizer for insecure Basic (base64-encoded plaintext) authentication.
761
 
 
762
 
    This form of authentication is broken and insecure.  Do not use it.
763
 
    """
764
 
 
765
 
    implements(IAuthorizer)
766
 
 
767
 
    def getChallenge(self, peer):
768
 
        return None
769
 
 
770
 
    def decode(self, response):
771
 
        # At least one SIP client improperly pads its Base64 encoded messages
772
 
        for i in range(3):
773
 
            try:
774
 
                creds = (response + ('=' * i)).decode('base64')
775
 
            except:
776
 
                pass
777
 
            else:
778
 
                break
779
 
        else:
780
 
            # Totally bogus
781
 
            raise SIPError(400)
782
 
        p = creds.split(':', 1)
783
 
        if len(p) == 2:
784
 
            return credentials.UsernamePassword(*p)
785
 
        raise SIPError(400)
786
 
 
787
 
class DigestedCredentials(credentials.UsernameHashedPassword):
788
 
    """Yet Another Simple Digest-MD5 authentication scheme"""
789
 
 
790
 
    def __init__(self, username, fields, challenges):
791
 
        self.username = username
792
 
        self.fields = fields
793
 
        self.challenges = challenges
794
 
 
795
 
    def checkPassword(self, password):
796
 
        method = 'REGISTER'
797
 
        response = self.fields.get('response')
798
 
        uri = self.fields.get('uri')
799
 
        nonce = self.fields.get('nonce')
800
 
        cnonce = self.fields.get('cnonce')
801
 
        nc = self.fields.get('nc')
802
 
        algo = self.fields.get('algorithm', 'MD5')
803
 
        qop = self.fields.get('qop', 'auth')
804
 
        opaque = self.fields.get('opaque')
805
 
 
806
 
        if opaque not in self.challenges:
807
 
            return False
808
 
        del self.challenges[opaque]
809
 
 
810
 
        user, domain = self.username.split('@', 1)
811
 
        if uri is None:
812
 
            uri = 'sip:' + domain
813
 
 
814
 
        expected = DigestCalcResponse(
815
 
            DigestCalcHA1(algo, user, domain, password, nonce, cnonce),
816
 
            nonce, nc, cnonce, qop, method, uri, None,
817
 
        )
818
 
 
819
 
        return expected == response
820
 
 
821
 
class DigestAuthorizer:
822
 
    CHALLENGE_LIFETIME = 15
823
 
 
824
 
    implements(IAuthorizer)
825
 
 
826
 
    def __init__(self):
827
 
        self.outstanding = {}
828
 
 
829
 
    def generateNonce(self):
830
 
        c = tuple([random.randrange(sys.maxint) for _ in range(3)])
831
 
        c = '%d%d%d' % c
832
 
        return c
833
 
 
834
 
    def generateOpaque(self):
835
 
        return str(random.randrange(sys.maxint))
836
 
 
837
 
    def getChallenge(self, peer):
838
 
        c = self.generateNonce()
839
 
        o = self.generateOpaque()
840
 
        self.outstanding[o] = c
841
 
        return ','.join((
842
 
            'nonce="%s"' % c,
843
 
            'opaque="%s"' % o,
844
 
            'qop="auth"',
845
 
            'algorithm="MD5"',
846
 
        ))
847
 
 
848
 
    def decode(self, response):
849
 
        def unq(s):
850
 
            if s[0] == s[-1] == '"':
851
 
                return s[1:-1]
852
 
            return s
853
 
        response = ' '.join(response.splitlines())
854
 
        parts = response.split(',')
855
 
        auth = dict([(k.strip(), unq(v.strip())) for (k, v) in [p.split('=', 1) for p in parts]])
856
 
        try:
857
 
            username = auth['username']
858
 
        except KeyError:
859
 
            raise SIPError(401)
860
 
        try:
861
 
            return DigestedCredentials(username, auth, self.outstanding)
862
 
        except:
863
 
            raise SIPError(400)
864
 
 
865
 
def responseFromRequest(code, request):
866
 
       response = Response(code)
867
 
       for name in ("via", "to", "from", "call-id", "cseq"):
868
 
           response.headers[name] = request.headers.get(name, [])[:]
869
 
 
870
 
       return response
871
 
 
872
 
def computeBranch(msg):
873
 
        """Create a branch tag to uniquely identify this message.  See
874
 
        RFC3261 sections 8.1.1.7 and 16.6.8."""
875
 
        if msg.headers.has_key('via') and msg.headers['via']:
876
 
            oldvia = msg.headers['via'][0]
877
 
        else:
878
 
            oldvia = ''
879
 
        return VIA_COOKIE + md5.new((parseAddress(msg.headers['to'][0])[2].get('tag','') +
880
 
                                    parseAddress(msg.headers['from'][0])[2].get('tag','')+
881
 
                                   msg.headers['call-id'][0] +
882
 
                                   msg.uri.toString() +
883
 
                                   oldvia +  
884
 
                                   msg.headers['cseq'][0].split(' ')[0])
885
 
                                  ).hexdigest()
886
 
 
887
 
class IContact(Interface):
888
 
    """A user of a registrar or proxy"""
889
 
 
890
 
    def registerAddress(physicalURL, expiry):
891
 
        """Register the physical address of a logical URL.
892
 
 
893
 
        @return: Deferred of C{Registration} or failure with RegistrationError.
894
 
        """
895
 
 
896
 
    def unregisterAddress():
897
 
        """Unregister the physical address of a logical URL.
898
 
 
899
 
        @return: Deferred of C{Registration} or failure with RegistrationError.
900
 
        """
901
 
 
902
 
    def getRegistrationInfo():
903
 
        """Get registration info for logical URL.
904
 
 
905
 
        @return: Deferred of C{Registration} object or failure of SIPLookupError.
906
 
        """
907
 
 
908
 
    def callIncoming(name, callerURI, callerContact):
909
 
        """Record an incoming call with a user's name, the incoming
910
 
        SIP URI, and, if they are registered with our system, their
911
 
        caller IContact implementor.
912
 
 
913
 
        You may *decline* an incoming call by raising an exception in
914
 
        this method.  A SIPError is preferred.
915
 
        """
916
 
 
917
 
    def callOutgoing(name, calleeURI):
918
 
        """Record an outgoing call.
919
 
        """
920
 
 
921
 
 
922
 
T1 = 0.5
923
 
T2 = 4
924
 
T4 = 5
925
 
 
926
 
class ClientInviteTransaction(object):
927
 
    __metaclass__ = ModalType
928
 
    initialMode = 'calling'
929
 
    modeAttribute = 'mode'
930
 
    
931
 
    def __init__(self, transport, tu, invite, peerURL):
932
 
        self.transport = transport
933
 
        self.tu = tu
934
 
        self.request = invite
935
 
        self.peer = peerURL
936
 
        self.response = None
937
 
        self.waitingToCancel = False
938
 
        self.branch = computeBranch(invite)
939
 
        self.transport.clientTransactions[self.branch] = self
940
 
        self.start()
941
 
        
942
 
    def transitionTo(self, stateName):
943
 
        self.end()
944
 
        self.mode = stateName
945
 
        self.start()
946
 
 
947
 
    def sendInvite(self):
948
 
        self.transport.sendRequest(self.request, self.peer)
949
 
 
950
 
    def transportError(self, err):
951
 
        self.tu.transportError(self, err)
952
 
        self.transitionTo('terminated')
953
 
        
954
 
 
955
 
    def ack(self, msg):        
956
 
        "Builds an ACK according to the rules in 17.1.1.3, RFC3261."
957
 
        ack = Request('ACK',self.request.uri)
958
 
        for name in ("from", "call-id", 'route'):
959
 
            ack.headers[name] = self.request.headers.get(name, [])[:]
960
 
        ack.addHeader('cseq', "%s ACK" % self.request.headers['cseq'][0].split(' ',1)[0])
961
 
        ack.headers['to'] = msg.headers['to']
962
 
        ack.headers['max-forwards'] = ['70']
963
 
        ack.addHeader('via', Via(self.transport.host,
964
 
                                  self.transport.port,
965
 
                                  rport=True,
966
 
                                  branch=self.branch).toString())
967
 
        self.transport.sendRequest(ack, self.peer)
968
 
 
969
 
    def sendCancel(self):
970
 
        cancel = Request("CANCEL", self.request.uri)
971
 
        for hdr in ('from','to','call-id'):
972
 
            cancel.addHeader(hdr, self.request.headers[hdr][0])
973
 
        cancel.addHeader('max-forwards','70')
974
 
        cancel.addHeader('cseq', "%s CANCEL" % self.request.headers['cseq'][0].split(' ',1)[0])
975
 
        cancel.addHeader('via', Via(self.transport.host,
976
 
                                    self.transport.port,
977
 
                                    rport=True,
978
 
                                    branch=self.branch).toString())
979
 
        self.transport.sendRequest(cancel, self.peer)
980
 
 
981
 
    
982
 
    class calling(mode):
983
 
 
984
 
        def start(self):
985
 
            debug("ClientInvite %s transitioning to 'calling'" % (self.peer,))
986
 
            self.timerATries = 0
987
 
 
988
 
            def timerARetry():
989
 
                self.timerATries +=1
990
 
                self.sendInvite()
991
 
                self.timerA = reactor.callLater(self.timerATries*T1,
992
 
                                                timerARetry)
993
 
            timerARetry()
994
 
            
995
 
            self.timerB = reactor.callLater(64*T1, self.timeout)
996
 
 
997
 
        def messageReceived(self, msg):
998
 
            self.response = msg
999
 
            self.tu.responseReceived(msg,self)
1000
 
            if 100 <= msg.code < 200:
1001
 
                if self.waitingToCancel:
1002
 
                    self.sendCancel()
1003
 
                    return
1004
 
                self.transitionTo('proceeding')
1005
 
            elif 200 <= msg.code < 300:
1006
 
                self.transitionTo('terminated')
1007
 
            elif 300 <= msg.code < 700:
1008
 
                self.ack(msg)
1009
 
                self.transitionTo('completed')
1010
 
 
1011
 
 
1012
 
        def end(self):
1013
 
            if self.timerA.active():
1014
 
                self.timerA.cancel()
1015
 
            if self.timerB.active():
1016
 
                self.timerB.cancel()
1017
 
 
1018
 
        def timeout(self):
1019
 
            if self.waitingToCancel:
1020
 
                self.response = responseFromRequest(487, self.request)
1021
 
            else:
1022
 
                self.response = responseFromRequest(408, self.request)
1023
 
            self.transitionTo('terminated')
1024
 
 
1025
 
        def cancel(self):
1026
 
            self.waitingToCancel = True
1027
 
            
1028
 
    class proceeding(mode):
1029
 
        def start(self):
1030
 
            debug("ClientInvite %s transitioning to 'proceeding'" % (self.peer,))
1031
 
 
1032
 
        def messageReceived(self, msg):
1033
 
            self.response = msg
1034
 
            self.tu.responseReceived(msg, self)
1035
 
            if 100 <= msg.code < 200:
1036
 
                pass
1037
 
            elif 200 <= msg.code < 300:
1038
 
                self.transitionTo('terminated')
1039
 
            elif 300 <= msg.code < 700:
1040
 
                self.ack(msg)
1041
 
                self.transitionTo('completed')
1042
 
 
1043
 
        def end(self):
1044
 
            if self.timerB.active():
1045
 
                self.timerB.cancel()
1046
 
 
1047
 
        def cancel(self):
1048
 
            self.sendCancel()
1049
 
            #not exactly timer B but it oughta ba
1050
 
            self.timerB = reactor.callLater(64*T1, self.cancel)
1051
 
 
1052
 
 
1053
 
 
1054
 
        def timeout(self):
1055
 
            if self.waitingToCancel:
1056
 
                self.response = responseFromRequest(487, self.request)
1057
 
            else:
1058
 
                self.response = responseFromRequest(408, self.request)
1059
 
            self.transitionTo('terminated')
1060
 
            
1061
 
    class completed(mode):
1062
 
 
1063
 
        def start(self):
1064
 
            debug("ClientInvite %s transitioning to 'completed'" % (self.peer,))
1065
 
            self.timerD = reactor.callLater(32, self.transitionTo,
1066
 
                                            'terminated')
1067
 
 
1068
 
        def messageReceived(self, msg):
1069
 
            if 300 <= msg.code:
1070
 
                self.ack(msg)                
1071
 
 
1072
 
        def end(self):
1073
 
            if self.timerD.active():
1074
 
                self.timerD.cancel()
1075
 
 
1076
 
        def cancel(self):
1077
 
            pass
1078
 
 
1079
 
    class terminated(mode):
1080
 
 
1081
 
        def start(self):
1082
 
            debug("ClientInvite %s transitioning to 'terminated'" % (self.peer,))
1083
 
            #XXX brutal
1084
 
            for k, v in self.transport.clientTransactions.iteritems():
1085
 
                if v == self:
1086
 
                    del self.transport.clientTransactions[k]
1087
 
                    break
1088
 
            self.tu.clientTransactionTerminated(self)
1089
 
            
1090
 
        def messageReceived(self, msg):
1091
 
            pass
1092
 
 
1093
 
        def end(self):
1094
 
            raise RuntimeError, "can't unterminate a transaction"
1095
 
        
1096
 
        def cancel(self):
1097
 
            pass
1098
 
        
1099
 
 
1100
 
 
1101
 
class ClientTransaction(object):
1102
 
    __metaclass__ = ModalType
1103
 
    initialMode = 'trying'
1104
 
    modeAttribute = 'mode'
1105
 
    def __init__(self, transport, tu, request, peerURL):
1106
 
        self.tu = tu
1107
 
        self.transport = transport
1108
 
        self.request = request
1109
 
        self.peer = peerURL
1110
 
        self.response = None
1111
 
        branch = computeBranch(request)
1112
 
        self.transport.clientTransactions[branch] = self
1113
 
        self.start()
1114
 
        
1115
 
    def transitionTo(self, stateName):
1116
 
        self.end()
1117
 
        self.mode = stateName
1118
 
        self.start()
1119
 
 
1120
 
    def transportError(self, err):
1121
 
        self.tu.transportError(self, err)
1122
 
        self.transitionTo('terminated')
1123
 
 
1124
 
    def sendRequest(self):
1125
 
        self.transport.sendRequest(self.request, self.peer)
1126
 
 
1127
 
    class trying(mode):
1128
 
 
1129
 
        def start(self):
1130
 
            debug("Client %s transitioning to 'trying'" % (self.peer,))
1131
 
            self.timerETries = 0
1132
 
            def timerERetry():                
1133
 
                self.timerETries += 1
1134
 
                self.sendRequest()
1135
 
                self.timerE = reactor.callLater(min((2**self.timerETries)*T1, T2),
1136
 
                                  timerERetry)
1137
 
            timerERetry()
1138
 
            self.timerF = reactor.callLater(64*T1, self.transitionTo, 'terminated')
1139
 
        
1140
 
        def messageReceived(self, msg):
1141
 
            if 200 <= msg.code:
1142
 
                self.response = msg
1143
 
                self.transitionTo('completed')
1144
 
            else:
1145
 
                self.transitionTo('proceeding')
1146
 
            self.tu.responseReceived(msg, self)
1147
 
 
1148
 
        
1149
 
        def end(self):
1150
 
            if self.timerE.active():
1151
 
                self.timerE.cancel()
1152
 
            if self.timerF.active():
1153
 
                self.timerF.cancel()
1154
 
            
1155
 
    class proceeding(mode):
1156
 
 
1157
 
        def start(self):
1158
 
            debug("Client %s transitioning to 'proceeding'" % (self.peer,))
1159
 
            self.timerETries = 0
1160
 
            def timerERetry():                
1161
 
                self.timerETries += 1
1162
 
                self.sendRequest()
1163
 
                reactor.callLater(T2, timerERetry)
1164
 
            timerERetry()
1165
 
            self.timerF = reactor.callLater(64*T1, self.transitionTo, 'terminated')
1166
 
            
1167
 
        def messageReceived(self, msg):                            
1168
 
            if 200 <= msg.code:
1169
 
                self.transitionTo('completed')
1170
 
                self.response = msg
1171
 
            self.tu.responseReceived(msg, self)
1172
 
            
1173
 
        def end(self):
1174
 
            if self.timerE.active():
1175
 
                self.timerE.cancel()
1176
 
            if self.timerF.active():
1177
 
                self.timerF.cancel()
1178
 
            
1179
 
    class completed(mode):
1180
 
 
1181
 
        def start(self):
1182
 
            debug("Client %s transitioning to 'completed'" % (self.peer,))
1183
 
            self.timerK = reactor.callLater(T4, self.transitionTo,
1184
 
                                            'terminated')
1185
 
 
1186
 
        def messageReceived(self, msg):
1187
 
            """
1188
 
            The "Completed" state exists to buffer any additional response
1189
 
            retransmissions that may be received (which is why the client
1190
 
            transaction remains there only for unreliable transports).
1191
 
            """
1192
 
        def end(self):
1193
 
            if self.timerK.active():
1194
 
                self.timerK.cancel()
1195
 
                
1196
 
 
1197
 
    class terminated(mode):
1198
 
        def start(self):
1199
 
            debug("Client %s transitioning to 'terminated'" % (self.peer,))
1200
 
            #XXX brutal
1201
 
            for k, v in self.transport.clientTransactions.iteritems():
1202
 
                if v == self:
1203
 
                    del self.transport.clientTransactions[k]
1204
 
                    break
1205
 
            self.tu.clientTransactionTerminated(self)
1206
 
 
1207
 
        def messageReceived(self, msg):
1208
 
            pass
1209
 
 
1210
 
        def end(self):
1211
 
            raise RuntimeError("can't unterminate a transaction")
1212
 
 
1213
 
class ServerInviteTransaction(object):
1214
 
    __metaclass__ = ModalType
1215
 
    initialMode = 'proceeding'
1216
 
    modeAttribute = 'mode'
1217
 
    
1218
 
    def __init__(self, transport, tu, message, peerURL):
1219
 
        self.message = message
1220
 
        self.tu = tu
1221
 
        self.transport = transport
1222
 
        self.peer = peerURL
1223
 
        self.lastResponse = None
1224
 
 
1225
 
    def sentFinalResponse(self):
1226
 
        return self.lastResponse.code >= 200
1227
 
    
1228
 
    def transitionTo(self, stateName):
1229
 
        self.end()
1230
 
        self.mode = stateName
1231
 
        self.start()
1232
 
 
1233
 
    def send100(self, msg):
1234
 
        self.sendResponse(responseFromRequest(100, msg))
1235
 
 
1236
 
    def respond(self, msg):
1237
 
        self.lastResponse = msg
1238
 
        self.transport.sendResponse(msg)
1239
 
 
1240
 
    def repeatLastResponse(self):
1241
 
        self.transport.sendResponse(self.lastResponse)
1242
 
 
1243
 
    class proceeding(mode):
1244
 
 
1245
 
        def start(self):
1246
 
            debug("ServerInvite %s transitioning to 'proceeding'" % (self.peer,))
1247
 
 
1248
 
        def end(self):
1249
 
            pass
1250
 
        
1251
 
        def messageReceived(self, msg):
1252
 
            if msg.method == "INVITE":
1253
 
                self.repeatLastResponse()
1254
 
                
1255
 
        def messageReceivedFromTU(self, msg):
1256
 
            self.respond(msg)
1257
 
            if 200 <= msg.code < 300:
1258
 
                self.transitionTo('terminated')
1259
 
            elif 300 <= msg.code < 700:
1260
 
                self.transitionTo('completed')
1261
 
    
1262
 
 
1263
 
    class completed(mode):
1264
 
 
1265
 
        def start(self):
1266
 
            debug("ServerInvite %s transitioning to 'completed'" % (self.peer,))
1267
 
            self.timerGTries = 1
1268
 
            def timerGRetry():
1269
 
                self.timerGTries +=1
1270
 
                self.repeatLastResponse()
1271
 
                self.timerG = reactor.callLater(min((2**self.timerGTries)*T1,
1272
 
                                                    T2), timerGRetry)
1273
 
            self.timerG = reactor.callLater(T1, timerGRetry)
1274
 
            self.timerH = reactor.callLater(64*T1,
1275
 
                                            self.transitionTo, 'terminated')
1276
 
 
1277
 
 
1278
 
        def messageReceived(self, msg):
1279
 
            if msg.method == "INVITE":
1280
 
                self.repeatLastResponse()
1281
 
            elif msg.method == "ACK":
1282
 
                self.transitionTo('confirmed')
1283
 
 
1284
 
        def messageReceivedFromTU(self, msg):
1285
 
            pass
1286
 
 
1287
 
        def end(self):
1288
 
            if self.timerG.active():
1289
 
                self.timerG.cancel()
1290
 
            if self.timerH.active():
1291
 
                self.timerH.cancel()
1292
 
 
1293
 
 
1294
 
    class confirmed(mode):
1295
 
 
1296
 
        def start(self):
1297
 
            debug("ServerInvite %s transitioning to 'confirmed'" % (self.peer,)) 
1298
 
            self.timerI = reactor.callLater(T4, self.transitionTo,
1299
 
                                            'terminated')
1300
 
 
1301
 
        def messageReceived(self, msg):
1302
 
            pass
1303
 
 
1304
 
        def messageReceivedFromTU(self, msg):
1305
 
            pass
1306
 
 
1307
 
        def end(self):
1308
 
            pass
1309
 
        
1310
 
 
1311
 
    class terminated(mode):
1312
 
 
1313
 
        def start(self):
1314
 
            debug("ServerInvite %s transitioning to 'terminated'" % (self.peer,))
1315
 
            self.transport.serverTransactionTerminated(self)
1316
 
        
1317
 
        def messageReceived(self, msg):
1318
 
            pass
1319
 
 
1320
 
        def messageReceivedFromTU(self, msg):
1321
 
            pass
1322
 
 
1323
 
        def end(self):
1324
 
            pass
1325
 
        
1326
 
 
1327
 
 
1328
 
class ServerTransaction(object):
1329
 
    __metaclass__ = ModalType
1330
 
    initialMode = 'trying'
1331
 
    modeAttribute = 'mode'
1332
 
 
1333
 
    def __init__(self, transport, tu, message, peerURL):
1334
 
        self.message = message
1335
 
        self.transport = transport
1336
 
        self.tu = tu
1337
 
        self.peer = peerURL
1338
 
        self.lastResponse = None
1339
 
 
1340
 
    def transitionTo(self, stateName):
1341
 
        self.end()
1342
 
        self.mode = stateName
1343
 
        self.start()
1344
 
 
1345
 
    def repeatLastResponse(self):
1346
 
        self.transport.sendResponse(self.lastResponse)
1347
 
 
1348
 
    def respond(self, msg):
1349
 
        self.lastResponse = msg
1350
 
        self.transport.sendResponse(msg)
1351
 
 
1352
 
    class trying(mode):
1353
 
 
1354
 
        def start(self):
1355
 
            debug("Server %s transitioning to 'trying'" % (self.peer,))
1356
 
 
1357
 
        def messageReceived(self, msg):
1358
 
            pass
1359
 
 
1360
 
        def messageReceivedFromTU(self, msg):
1361
 
            self.respond(msg)
1362
 
            if 100 <= msg.code < 200:
1363
 
                self.transitionTo('proceeding')
1364
 
            else:
1365
 
                self.transitionTo('completed')
1366
 
 
1367
 
        def end(self):
1368
 
            pass        
1369
 
 
1370
 
    class proceeding(mode):
1371
 
 
1372
 
        def start(self):
1373
 
            debug("Server %s transitioning to 'proceeding'" % (self.peer,))
1374
 
 
1375
 
        def messageReceived(self, msg):
1376
 
            self.repeatLastResponse()
1377
 
            
1378
 
        def messageReceivedFromTU(self, msg):
1379
 
            self.respond(msg)
1380
 
            if 200 <= msg.code < 700:                
1381
 
                self.transitionTo('completed')    
1382
 
 
1383
 
 
1384
 
        def end(self):
1385
 
            pass        
1386
 
 
1387
 
    class completed(mode):
1388
 
 
1389
 
        def start(self):
1390
 
            debug("Server %s transitioning to 'completed'" % (self.peer,))
1391
 
            self.timerJ = reactor.callLater(64*T1,
1392
 
                                            self.transitionTo, 'terminated')
1393
 
 
1394
 
        def messageReceived(self, msg):
1395
 
            self.repeatLastResponse()
1396
 
 
1397
 
        def messageReceivedFromTU(self, msg):
1398
 
            pass
1399
 
 
1400
 
        def end(self):
1401
 
            if self.timerJ.active():
1402
 
                self.timerJ.cancel()
1403
 
 
1404
 
 
1405
 
    class terminated(mode):
1406
 
 
1407
 
        def start(self):
1408
 
            debug("Server %s transitioning to 'terminated'" % (self.peer,))
1409
 
            self.transport.serverTransactionTerminated(self)
1410
 
        
1411
 
        def messageReceived(self, msg):
1412
 
            pass
1413
 
 
1414
 
        def messageReceivedFromTU(self, msg):
1415
 
            pass
1416
 
 
1417
 
        def end(self):
1418
 
            pass
1419
 
        
1420
 
 
1421
 
    
1422
 
    
1423
 
###############################################################################
1424
 
 
1425
 
class SIPTransport(protocol.DatagramProtocol):
1426
 
 
1427
 
    PORT = PORT
1428
 
    debug = debuggingEnabled
1429
 
 
1430
 
    def __init__(self, tu, hosts, port):
1431
 
        """tu: an implementor of ITransactionUser.
1432
 
           hosts: A sequence of hostnames this element is
1433
 
                  authoritative for. The first is used as the name for
1434
 
                  outgoing messages. If empty, socket.getfqdn() is
1435
 
                  used instead.                  
1436
 
           port: The port this element listens on."""
1437
 
        
1438
 
        self.messages = []
1439
 
        self.parser = MessagesParser(self.addMessage)
1440
 
        self.tu = tu
1441
 
        self.hosts = hosts or [socket.getfqdn()]
1442
 
        self.host = self.hosts[0]
1443
 
        self.port = port
1444
 
        self.serverTransactions = {}
1445
 
        self.clientTransactions = {}
1446
 
        tu.start(self)
1447
 
        
1448
 
    def addMessage(self, msg):
1449
 
        self.messages.append(msg)
1450
 
 
1451
 
    def datagramReceived(self, data, addr):
1452
 
        try:
1453
 
            self.parser.dataReceived(data)
1454
 
            self.parser.dataDone()
1455
 
            try:
1456
 
                for m in self.messages:
1457
 
                    if self.debug:
1458
 
                        if isinstance(m, Request):
1459
 
                            id = m.method
1460
 
                        else:
1461
 
                            id = m.code
1462
 
                        debug("Received %r from %r." % (id, addr))
1463
 
                    if isinstance(m, Request):
1464
 
                        self._fixupNAT(m, addr)
1465
 
                        self.handle_request(m, addr)
1466
 
                    else:
1467
 
                        self.handle_response(m, addr)
1468
 
            finally:
1469
 
                del self.messages[:]
1470
 
        except Exception, e:
1471
 
            log.err()
1472
 
            if debuggingEnabled:
1473
 
                raise
1474
 
            else:
1475
 
                self._badRequest(addr, e)
1476
 
            
1477
 
    def _badRequest(self, addr, e):
1478
 
        #request parsing failed, we're going to have to make stuff up
1479
 
 
1480
 
        if isinstance(e, SIPError):
1481
 
            code = e.code
1482
 
        else:
1483
 
            code = 500
1484
 
        r = Response(code)
1485
 
 
1486
 
        r.addHeader("to", "%s:%s" % (addr))
1487
 
        # see RFC3261 8.1.1.7, 16.6.8
1488
 
        r.addHeader("via", Via(host=self.host, port=self.port, branch=VIA_COOKIE+ md5.new(repr(addr)).hexdigest()).toString())
1489
 
        self.transport.write(r, addr)
1490
 
 
1491
 
    def _fixupNAT(self, message, (srcHost, srcPort)):
1492
 
        # RFC 3581
1493
 
        senderVia = parseViaHeader(message.headers["via"][0])
1494
 
        senderVia.received = srcHost
1495
 
        if senderVia.rport == True:
1496
 
            senderVia.rport = srcPort
1497
 
        message.headers["via"][0] = senderVia.toString()
1498
 
 
1499
 
    def handle_request(self, msg, addr):
1500
 
        #RFC 3261 17.2.3
1501
 
        via = parseViaHeader(msg.headers['via'][0])
1502
 
        
1503
 
        if not (via.branch and via.branch.startswith(VIA_COOKIE)):
1504
 
            via.branch = computeBranch(msg)
1505
 
        method = msg.method
1506
 
        if method == "ACK":
1507
 
            method = "INVITE"
1508
 
        st = self.serverTransactions.get((via.branch, via.host,
1509
 
                                          via.port, method))
1510
 
        if st:
1511
 
            st.messageReceived(msg)
1512
 
        else:
1513
 
            def addNewServerTransaction(st):
1514
 
                if st:
1515
 
                    self.serverTransactions[(via.branch, via.host,
1516
 
                                             via.port, msg.method)] = st
1517
 
            return defer.maybeDeferred(self.tu.requestReceived, msg, addr
1518
 
                                       ).addCallback(addNewServerTransaction)
1519
 
    
1520
 
 
1521
 
    def serverTransactionTerminated(self, st):
1522
 
        #Brutal, but simple
1523
 
        for k,v in self.serverTransactions.iteritems():
1524
 
            if st == v:
1525
 
                del self.serverTransactions[k]
1526
 
                break
1527
 
 
1528
 
 
1529
 
    def handle_response(self, msg, addr):
1530
 
        #RFC 3261 18.1.2
1531
 
        via = parseViaHeader(msg.headers['via'][0])
1532
 
        if not (via.host in self.hosts and via.port == self.port):
1533
 
            #drop silently
1534
 
            return
1535
 
        
1536
 
        #RFC 3261 17.1.3
1537
 
        ct = self.clientTransactions.get(via.branch)        
1538
 
        if ct and msg.headers['cseq'][0].split(' ')[1] == ct.request.headers['cseq'][0].split(' ')[1]:
1539
 
            ct.messageReceived(msg)
1540
 
        else:
1541
 
            self.tu.responseReceived(msg)
1542
 
 
1543
 
    
1544
 
    def sendRequest(self, msg, target):
1545
 
        """Add a Via header to this message and send it to the (host,
1546
 
        port) target."""
1547
 
        
1548
 
        #CANCEL & ACK requires the same Via branch as the thing it is
1549
 
        #cancelling/acking so it has to be added by the txn            
1550
 
        if msg.method not in ("ACK", "CANCEL"):
1551
 
            #raaaaa this is so we don't add the same via header on resends
1552
 
            msg = msg.copy()
1553
 
            if msg.headers.get('via'):
1554
 
                msg.headers['via'] = msg.headers['via'][:]
1555
 
            #RFC 3261 18.1.1
1556
 
            #RFC 3581 3            
1557
 
            msg.headers.setdefault('via', []).insert(0, Via(self.host, self.port,
1558
 
                                            rport=True,
1559
 
                                            branch=computeBranch(msg)).toString())
1560
 
        txt = msg.toString()
1561
 
        if len(txt) > 1300:
1562
 
            raise NotImplementedError, "Message too big for UDP. You're boned."
1563
 
        debug("Sending %r to %r" % (msg.method, target))
1564
 
        self._resolveA(target[0]).addCallback(
1565
 
            lambda ip: self.sendMessage(msg, (ip, (target[1] or self.PORT))))
1566
 
        
1567
 
    
1568
 
    def sendResponse(self, msg):
1569
 
        """Determine the target for the response and send it."""
1570
 
        
1571
 
        #RFC 3261 18.2.2
1572
 
        #RFC 3581 4
1573
 
        via = parseViaHeader(msg.headers['via'][0])
1574
 
        host = via.received or via.host
1575
 
        port = via.rport or via.port or self.PORT
1576
 
        
1577
 
        debug("Sending %r to %r" % (msg.code, (host, port)))
1578
 
        self._resolveA(host).addCallback(
1579
 
            lambda ip: self.sendMessage(msg, (ip, port)))
1580
 
 
1581
 
    def sendMessage(self, msg, (host, port)):
1582
 
        #for easier testing
1583
 
        self.transport.write(msg.toString(), (host, port))
1584
 
 
1585
 
    def _resolveA(self, addr):
1586
 
            return reactor.resolve(addr)
1587
 
 
1588
 
 
1589
 
class ITransactionUser(Interface):
1590
 
    def start(transport):
1591
 
        """Connects the transport to the TU."""
1592
 
 
1593
 
    def requestReceived(msg, addr):
1594
 
        """Processes a message, after the transport and transaction
1595
 
        layer are finished with it. May return a ServerTransaction (or
1596
 
        ServerInviteTransaction), which will handle subsequent
1597
 
        messages from that SIP transaction."""
1598
 
 
1599
 
    def responseReceived(msg, ct=None):
1600
 
        """Processes a response received from the transport, along
1601
 
        with the client transaction it is a part of, if any."""
1602
 
 
1603
 
 
1604
 
    def clientTransactionTerminated(ct):
1605
 
        """Called when a client transaction created by this TU
1606
 
        transitions to the 'terminated' state."""
1607
 
        
1608
 
#from resiprocate's proxy
1609
 
responsePriorities = {                  428: 24, 429: 24, 494: 24, 
1610
 
    412: 1,                             413: 25, 414: 25, 
1611
 
    484: 2,                             421: 26, 
1612
 
    422: 3, 423: 3,                     486: 30, 
1613
 
    407: 4, 401: 4,                     480: 31, 
1614
 
    402: 6,                             410: 32,                       
1615
 
    493: 10,                            436: 33, 437: 33, 
1616
 
    420: 12,                            403: 32, 
1617
 
    406: 13, 415: 13, 488: 13,          404: 35, 
1618
 
    416: 20, 417: 20,                   487: 36, 
1619
 
    405: 21, 501: 21,                   503: 40, 
1620
 
    580: 22,                            483: 41, 482: 41, 
1621
 
    485: 23,                            408: 49} 
1622
 
                                        
1623
 
 
1624
 
class Proxy:
1625
 
    implements(ITransactionUser)
1626
 
 
1627
 
    def __init__(self, portal):
1628
 
        self.portal = portal
1629
 
        self.sessions = {}
1630
 
        self.responseContexts = {}
1631
 
        self.finalResponses = {}
1632
 
        self.registrar = Registrar(portal)
1633
 
 
1634
 
    def start(self, transport):
1635
 
        self.transport = transport
1636
 
        self.registrar.start(transport)
1637
 
        self.recordroute = URL(host=transport.host,
1638
 
                               port=transport.port, other={'lr':''})
1639
 
 
1640
 
    def requestReceived(self, msg, addr):
1641
 
        #RFC 3261 16.4
1642
 
        if msg.uri == self.recordroute:
1643
 
            msg.uri = msg.headers['route'].pop()
1644
 
            
1645
 
        if msg.headers.get('route',None):
1646
 
            route = parseAddress(msg.headers['route'][0])[1]
1647
 
            if (route.host in self.transport.hosts and
1648
 
                (route.port or PORT) == self.transport.port):
1649
 
                del msg.headers['route'][0]
1650
 
 
1651
 
        #RFC 3261 16.5
1652
 
        if msg.uri.host in self.transport.hosts: 
1653
 
            if msg.method == 'REGISTER':
1654
 
                return self.registrar.requestReceived(msg, addr)
1655
 
            elif msg.method == 'OPTIONS' and msg.uri.username is None:
1656
 
                st = ServerTransaction(self.transport, self, msg, addr)
1657
 
                st.messageReceivedFromTU(responseFromRequest(200, msg))
1658
 
                return st
1659
 
 
1660
 
                
1661
 
        def _cb(x, st):
1662
 
            return self.findTargets(msg.uri).addCallback(
1663
 
                self.forwardRequest, msg, st)
1664
 
            
1665
 
        def _eb(err):
1666
 
            if err.check(SIPError):
1667
 
                errcode = err.value.code
1668
 
                err = None
1669
 
            else:
1670
 
                if debuggingEnabled:
1671
 
                    import pdb; pdb.set_trace()
1672
 
                errcode = 500
1673
 
                log.err(err)
1674
 
            st.messageReceivedFromTU(
1675
 
                responseFromRequest(errcode, msg))
1676
 
            
1677
 
        if msg.method == 'INVITE':            
1678
 
            st = ServerInviteTransaction(self.transport, self, msg, addr)
1679
 
            st.messageReceivedFromTU(responseFromRequest(100, msg))
1680
 
            return self.checkInviteAuthorization(msg).addCallback(
1681
 
                _cb, st).addErrback(_eb).addCallback(
1682
 
                lambda _: st)
1683
 
        elif msg.method == 'CANCEL':
1684
 
            via = parseViaHeader(msg.headers['via'][0])
1685
 
            self.transport.sendResponse(responseFromRequest(200, msg))
1686
 
            #RFC 3261 9.2
1687
 
            for k, t in self.transport.serverTransactions.iteritems():
1688
 
                if (isinstance(t, (ServerTransaction,ServerInviteTransaction))
1689
 
                    and (via.branch, via.host, via.port) == k[:3]):
1690
 
                    self.cancelPendingClients(t)
1691
 
                    break
1692
 
            else:
1693
 
                self.proxyRequestStatelessly(msg)        
1694
 
                return None
1695
 
        elif msg.method == 'ACK':
1696
 
            msg.headers['via'].insert(0,Via(self.transport.host, self.transport.port,
1697
 
                                            rport=True,
1698
 
                                            branch=computeBranch(msg)).toString())
1699
 
            self.proxyRequestStatelessly(msg)
1700
 
            return None
1701
 
        else:
1702
 
            st = ServerTransaction(self.transport, self, msg, addr)
1703
 
            _cb(None, st)
1704
 
            
1705
 
            if msg.method == 'BYE':
1706
 
                self.untrackSession(msg)
1707
 
            return st
1708
 
        
1709
 
    def proxyRequestStatelessly(self, originalMsg):        
1710
 
        def _cb(addrs):
1711
 
            msg, addr = self.processRouting(originalMsg, addrs[0])
1712
 
            self.transport.sendRequest(msg, (addr.host, addr.port))
1713
 
        self.findTargets(originalMsg.uri).addCallback(_cb)
1714
 
 
1715
 
    def findTargets(self, addr):
1716
 
        d = self.portal.login(Preauthenticated(addr.toCredString()),
1717
 
                              None, IContact)
1718
 
 
1719
 
        def lookedUpSuccessful((ifac, contact, logout)):
1720
 
            return defer.maybeDeferred(contact.getRegistrationInfo
1721
 
                                       ).addCallback(
1722
 
                lambda x: [i[0] for i in x])        
1723
 
        def failedLookup(err):
1724
 
            e = err.trap(NoSuchUser, UnauthorizedLogin)
1725
 
            if e == UnauthorizedLogin:
1726
 
                return [addr]
1727
 
            elif e == NoSuchUser:
1728
 
                raise SIPLookupError(604)
1729
 
        return d.addCallback(lookedUpSuccessful).addErrback(failedLookup)
1730
 
        
1731
 
    def forwardRequest(self, targets, originalMsg, st):
1732
 
        fs = []
1733
 
        if len(targets) == 0:
1734
 
            raise SIPLookupError(480)
1735
 
        for addr in targets:
1736
 
            msg, addr = self.processRouting(originalMsg, addr)
1737
 
            
1738
 
            fs.append(self._lookupURI(addr).addCallback(self._forward,msg, st))
1739
 
        return defer.DeferredList(fs)
1740
 
 
1741
 
    def processRouting(self, originalMsg, addr):
1742
 
        #16.6
1743
 
        msg = originalMsg.copy()
1744
 
        msg.uri = addr
1745
 
        if msg.headers.get('max-forwards'):                
1746
 
            msg.headers['max-forwards'][0] = str(int(
1747
 
                msg.headers['max-forwards'][0]) - 1)
1748
 
        else:
1749
 
            msg.headers['max-forwards'] = ['70']
1750
 
        msg.headers.setdefault('record-route',
1751
 
                               []).insert(0, self.recordroute.toString())
1752
 
        if msg.headers.get('route', None):
1753
 
            if 'lr' not in parseAddress(msg.headers['route'][0])[1].other:
1754
 
                #more coping with strict routers
1755
 
                msg.headers['route'].append(msg.uri.toString())
1756
 
                msg.uri = parseAddress(msg.headers['route'].pop())[1]
1757
 
            else:
1758
 
                addr = parseAddress(msg.headers['route'][0])[1]
1759
 
        return msg, addr
1760
 
    
1761
 
    def _forward(self, addresses, msg, st):
1762
 
        for address in addresses:
1763
 
            #16.6.8
1764
 
            if msg.method == 'INVITE':
1765
 
                ct = ClientInviteTransaction(self.transport,
1766
 
                                             self, msg, address)
1767
 
                timerC = reactor.callLater(181, ct.timeout)
1768
 
            else:
1769
 
                ct = ClientTransaction(self.transport, self, msg, address)
1770
 
                timerC=None
1771
 
 
1772
 
            self.responseContexts[ct] = (st, timerC)
1773
 
            self.responseContexts.setdefault(st, []).append(ct)
1774
 
    def _lookupURI(self, userURI):
1775
 
        """Leave this method: it is hooked by the tests.
1776
 
        """
1777
 
        #RFC 3263 4.2
1778
 
        if abstract.isIPAddress(userURI.host):
1779
 
            # it is an IP not a hostname
1780
 
            if not userURI.port:
1781
 
                userURI.port = 5060
1782
 
            return defer.succeed([(userURI.host, userURI.port)])
1783
 
        else:
1784
 
            if userURI.port is not None:
1785
 
                return defer.succeed([(userURI.host, userURI.port)])
1786
 
            d = client.lookupService('_sip._udp.' + userURI.host)
1787
 
            d.addCallback(self._resolveSRV, userURI)
1788
 
            return d
1789
 
 
1790
 
    def _resolveSRV(self, (answers, _, __), userURI):
1791
 
 
1792
 
        if answers:
1793
 
            answers = [(a.payload.priority, str(a.payload.target), a.payload.port) for a in answers]
1794
 
            answers.sort()
1795
 
            return [(answer[1], answer[2]) for answer in answers]
1796
 
        else:
1797
 
            #just do an A lookup
1798
 
            return [(userURI.host, 5060)]
1799
 
    
1800
 
    def checkInviteAuthorization(self, message):
1801
 
        name, uri, tags = parseAddress(message.headers["to"][0], clean=1)
1802
 
        fromname, fromuri, ignoredTags = parseAddress(message.headers["from"][0], clean=1)
1803
 
        somebodyWasAuthorized = []
1804
 
        def recordIt(oururi, theiruri, method, *extra):            
1805
 
            #XXX XXX totally need to check to see if the invite is
1806
 
            #from a registered address
1807
 
            d = self.portal.login(Preauthenticated('%s@%s' % (oururi.username, oururi.host)), None, IContact)
1808
 
            def success((iface, contact, logout), theiruri=theiruri, method=method):
1809
 
                somebodyWasAuthorized.append(contact)
1810
 
                getattr(contact,method)(name, theiruri, *extra)
1811
 
                return contact
1812
 
            def ignoreUnauthorized(failure):
1813
 
                failure.trap(UnauthorizedLogin)
1814
 
                return None
1815
 
 
1816
 
            return d.addCallbacks(success, ignoreUnauthorized)
1817
 
 
1818
 
        # this is a somewhat, uh, "compact" (some would say
1819
 
        # obfuscated) representation of a series of unfortunate
1820
 
        # events, so here is some prose to guide you through it:
1821
 
 
1822
 
        # First, we tell the caller (if they're registered with us) that
1823
 
        # they're making a call, to give them the opportunity to record it on
1824
 
        # the server.  they don't yet know whether their callee is registered
1825
 
        # or not, and in fact it doesn't matter to them (they _made_ the call
1826
 
        # without such information, after all).  A caller can potentially
1827
 
        # cancel the call at this point by raising an exception but I can't
1828
 
        # think of a reason to do that which isn't an error.
1829
 
 
1830
 
        # then, we tell the callee (if they're registered with us) that they're
1831
 
        # receiving a call.  At this point, they may *decline* the call by
1832
 
        # raising a SIPError of some kind in callIncoming.  This is the way
1833
 
        # you'll implement call screening.  ( TODO: You *also* ought to be able
1834
 
        # to implement a redirect on an incoming call by raising an appropriate
1835
 
        # SIPError, but there is not currently a way to get the URL for the
1836
 
        # redirect all the way back up the call chain. )
1837
 
 
1838
 
        # Before we relay this to the rest of the SIP logic, we make sure that
1839
 
        # at least ONE of the participants was authorized to make or receive
1840
 
        # this call through this server.  We don't want to proxy arbitrary
1841
 
        # third-party INVITE requests.
1842
 
 
1843
 
        def makeSureSomebodyWasAuthorized(value):
1844
 
            if not somebodyWasAuthorized:
1845
 
                raise SIPError(401)
1846
 
            return value
1847
 
 
1848
 
        return recordIt(fromuri, uri, 'callOutgoing').addCallback(
1849
 
            lambda caller:
1850
 
            recordIt(uri, fromuri, 'callIncoming', caller)
1851
 
            ).addCallback(makeSureSomebodyWasAuthorized)
1852
 
 
1853
 
 
1854
 
    def clientTransactionTerminated(self, ct):
1855
 
        st = self.responseContexts[ct]
1856
 
        #XXX kluge?
1857
 
        if not isinstance(st, ServerTransaction):
1858
 
            st, timerC = st
1859
 
        else:
1860
 
            timerC = None
1861
 
        cts = self.responseContexts[st]
1862
 
        for ct in cts:
1863
 
            if ct.mode != "terminated":
1864
 
                break
1865
 
        else:
1866
 
 
1867
 
            if not self.finalResponses.get(st, None):
1868
 
                response = self.chooseFinalResponse(st)
1869
 
                self.finalResponses[st] = response
1870
 
                st.messageReceivedFromTU(response)
1871
 
            del self.responseContexts[st]
1872
 
            if timerC: timerC.cancel()
1873
 
            for ct in cts:
1874
 
                del self.responseContexts[ct]
1875
 
 
1876
 
 
1877
 
            
1878
 
    def chooseFinalResponse(self, st):
1879
 
        cts = self.responseContexts[st]
1880
 
        noResponses = True
1881
 
        responses = [(ct.response and ct.response.code, ct) for ct in cts]
1882
 
        for code, ct in responses:
1883
 
            if code is not None and code >= 200:
1884
 
                noResponses = False
1885
 
                break
1886
 
        assert not noResponses, "BROKEN. chooseFinalResponse was called before any final responses occurred." 
1887
 
 
1888
 
        prioritizedResponses = []
1889
 
        for code, ct in responses:
1890
 
            prio = responsePriorities.get(code, None)
1891
 
            if prio:
1892
 
                prioritizedResponses.append((prio, ct.response))
1893
 
            elif 300 <= code < 400:
1894
 
                prioritizedResponses.append((5, ct.response))
1895
 
            elif 500 <= code < 600:
1896
 
                prioritizedResponses.append((42, ct.response))
1897
 
            else:
1898
 
                prioritizedResponses.append((43, ct.response))
1899
 
        prioritizedResponses.sort()
1900
 
        finalResponse = prioritizedResponses[0][1]
1901
 
 
1902
 
        #XXX need to process 3xx messages ourselves
1903
 
        #instead of forwarding them
1904
 
        if 300 <= finalResponse.code < 400:
1905
 
            for code, ct in responses:
1906
 
                if 300 <= code < 400:
1907
 
                    finalResponse.headers['contact'].extend(
1908
 
                        ct.response.headers['contact'])
1909
 
            finalResponse.code = 300
1910
 
 
1911
 
        elif finalResponse.code in (401, 407):
1912
 
            #RFC 3261 16.7.7                
1913
 
            authHeaders = {}
1914
 
            for code, ct in responses:
1915
 
                if code == 401:
1916
 
                    finalResponse.headers['www-authenticate'].extend(
1917
 
                        r.headers.get("www-authenticate", []))
1918
 
 
1919
 
 
1920
 
                elif code == 407:
1921
 
                    finalResponse.headers['proxy-authenticate'].extend(
1922
 
                        r.headers.get("proxy-authenticate",[]))
1923
 
                    finalResponse.code = 407
1924
 
        return finalResponse
1925
 
 
1926
 
 
1927
 
            
1928
 
    def responseReceived(self, msg, ct=None):
1929
 
        #RFC 3261 16.7
1930
 
        if msg.code == 100:
1931
 
            return
1932
 
        via = msg.headers['via'][0]
1933
 
        msg.headers['via'] = msg.headers['via'][1:]
1934
 
        
1935
 
        if len(msg.headers['via']) == 0:
1936
 
            if not msg.headers['cseq'][0].endswith('CANCEL'):
1937
 
                #ignore CANCEL/200s, process the rest
1938
 
                self.processLocalResponse(msg, ct)
1939
 
            return
1940
 
        if (not ct and msg.headers['cseq'][0].endswith('INVITE')
1941
 
            and 200 <= msg.code < 300): 
1942
 
            self.transport.sendResponse(msg)
1943
 
            return
1944
 
        else:
1945
 
            st, timerC = self.responseContexts.get(ct, (None, None))
1946
 
            if not st:
1947
 
                #staaaaateless
1948
 
                self.transport.sendResponse(msg)
1949
 
 
1950
 
        if 100 < msg.code < 200:
1951
 
            if isinstance(ct, ClientInviteTransaction):
1952
 
                timerC.reset(181)
1953
 
            st.messageReceivedFromTU(msg)
1954
 
            return
1955
 
        
1956
 
        #TODO: Catch 3xx responses, add their redirects to the target set
1957
 
        if 200 <= msg.code < 300:
1958
 
            self.finalResponses[st] = msg
1959
 
            reactor.callLater(0, self.cancelPendingClients, st)
1960
 
            if isinstance(ct, ClientInviteTransaction):
1961
 
                self.trackSession(msg)
1962
 
        elif 600 <= msg.code:
1963
 
            self.cancelPendingClients(st)            
1964
 
        else:
1965
 
            #might as well leave the message there, sans our via header
1966
 
            ct.response = msg
1967
 
        
1968
 
        st.messageReceivedFromTU(msg)
1969
 
        
1970
 
    def processLocalResponse(self, msg, ct):
1971
 
        if getattr(self.originator, None):
1972
 
            self.originator.responseReceived(msg, ct)
1973
 
                    
1974
 
    def cancelPendingClients(self, st):
1975
 
        if isinstance(st, ServerInviteTransaction):
1976
 
            cts = self.responseContexts.get(st, [])
1977
 
            for ct in cts:
1978
 
                ct.cancel()
1979
 
 
1980
 
    def trackSession(self, msg):
1981
 
        pass
1982
 
 
1983
 
    def untrackSession(self, msg):
1984
 
        pass
1985
 
 
1986
 
    
1987
 
class Registrar:
1988
 
    authorizers = {
1989
 
        'digest': DigestAuthorizer(),
1990
 
        }
1991
 
    def __init__(self, portal):
1992
 
        self.portal = portal
1993
 
 
1994
 
    def start(self, transport):
1995
 
        self.transport = transport
1996
 
        
1997
 
    def getRegistrationInfo(self, url):
1998
 
        #XXX Need to think about impact of all these cred lookups in a
1999
 
        #cluster environment
2000
 
        def _cbRegInfo((i,a,l)):
2001
 
            return a.getRegistrationInfo()
2002
 
        def _ebRegInfo(failure):
2003
 
            failure.trap(UnauthorizedLogin)
2004
 
            return None        
2005
 
        return self.portal.login(Preauthenticated('%s@%s' % (url.username,
2006
 
                                                            url.host)),
2007
 
                                 None, IContact).addCallback(
2008
 
            _cbRegInfo).addErrback(
2009
 
            _ebRegInfo)
2010
 
 
2011
 
    
2012
 
    def requestReceived(self, msg, addr):
2013
 
        st = ServerTransaction(self.transport, self, msg, addr)
2014
 
        if msg.method == "REGISTER":
2015
 
            self.registrate(msg, addr).addCallback(st.messageReceivedFromTU)
2016
 
        else:
2017
 
            st.messageReceivedFromTU(responseFromRequest(501, msg))
2018
 
        return st
2019
 
            
2020
 
    def registrate(self, message, addr):
2021
 
        name, toURL, params = parseAddress(message.headers["to"][0], clean=1)
2022
 
        if not message.headers.has_key("authorization"):
2023
 
            creds = credentials.UsernamePassword(toURL.toCredString(),'')
2024
 
        else:
2025
 
            parts = message.headers['authorization'][0].split(None, 1)
2026
 
            a = self.authorizers.get(parts[0].lower())
2027
 
            if a:
2028
 
                creds = a.decode(parts[1])                
2029
 
                # IGNORE the authorization username - take that, SIP
2030
 
                # configuration UIs!!!                
2031
 
                creds.username = toURL.toCredString()
2032
 
 
2033
 
        return self.portal.login(creds, None, IContact
2034
 
            ).addCallback(self._cbLogin, message, addr
2035
 
            ).addErrback(self._ebLogin, message, addr)
2036
 
 
2037
 
    def _cbLogin(self, (i, a, l), message, addr):
2038
 
        return self.register(a, message, addr)
2039
 
 
2040
 
    def _ebLogin(self, failure, message, addr):
2041
 
        failure.trap(UnauthorizedLogin)
2042
 
        return self.unauthorized(message, addr)
2043
 
 
2044
 
    def register(self, avatar, message, addr):
2045
 
        def _cbRegister(regdata, message):
2046
 
            response = responseFromRequest(200, message)            
2047
 
            #for old times' sake I will send a separate Expires header
2048
 
            #if there is only one contact            
2049
 
            if len(regdata) == 1:
2050
 
                contactURL, expiry = regdata[0]
2051
 
                response.addHeader("contact", contactURL.toString())
2052
 
                response.addHeader("expires", str(expiry))
2053
 
            else:
2054
 
                for contactURL, expiry in regdata:
2055
 
                    response.addHeader("contact", "%s;expires=%s" %
2056
 
                                       (contactURL.toString(), expiry))
2057
 
            response.addHeader("content-length", "0")
2058
 
            return response
2059
 
 
2060
 
        def _cbUnregister(regdata, message):
2061
 
 
2062
 
            msg = responseFromRequest(200, message)
2063
 
            #More backwards combatibility
2064
 
            if len(regdata) == 1:
2065
 
                contactURL, expiry = regdata[0]
2066
 
                msg.addHeader("contact", contactURL.toString())
2067
 
                msg.addHeader("expires", "0")
2068
 
            else:
2069
 
                for contactURL, expiry in regdata:
2070
 
                    msg.addHeader("contact", "%s;expires=%s" %
2071
 
                                  (contactURL.toString(), 0))
2072
 
            return msg
2073
 
 
2074
 
        def _ebUnregister(registration, message):
2075
 
            pass
2076
 
 
2077
 
        name, toURL, params = parseAddress(message.headers["to"][0], clean=1)
2078
 
        contact = None
2079
 
        if message.headers.has_key("contact"):
2080
 
            contact = message.headers["contact"][0]
2081
 
 
2082
 
        expires = message.headers.get("expires", [None])[0]
2083
 
        if expires == "0":
2084
 
            if contact == "*":
2085
 
                return defer.maybeDeferred(avatar.unregisterAllAddresses).addCallback(
2086
 
                    _cbUnregister, message
2087
 
                    ).addErrback(_ebUnregister, message)
2088
 
            else:
2089
 
                name, contactURL, params = parseAddress(contact) #host=addr.host, port=addr.port)
2090
 
                return defer.maybeDeferred(avatar.unregisterAddress,
2091
 
                                    contactURL).addCallback(
2092
 
                    _cbUnregister, message).addErrback(
2093
 
                    _ebUnregister, message)
2094
 
        else:
2095
 
            name, contactURL, params = parseAddress(contact)# host=addr.host, port=addr.port)
2096
 
            if contact is not None:
2097
 
                if expires:
2098
 
                    expiresInt = int(expires)
2099
 
                else:
2100
 
                    expiresInt = DEFAULT_REGISTRATION_LIFETIME
2101
 
                d = defer.maybeDeferred(avatar.registerAddress,
2102
 
                                        contactURL, expiresInt)
2103
 
            else:
2104
 
                d = defer.maybeDeferred(avatar.getRegistrationInfo)
2105
 
            d.addCallback(_cbRegister, message).addErrback(self._ebLogin,
2106
 
                                                           message, addr)
2107
 
            return d
2108
 
 
2109
 
    def unauthorized(self, message, addr):
2110
 
        # log.msg("Failed registration attempt for %s from %s" %
2111
 
        # (message.headers.get('from'), message.headers.get('contact')))
2112
 
        m = responseFromRequest(401, message)
2113
 
        for (scheme, auth) in self.authorizers.iteritems():
2114
 
            chal = auth.getChallenge(addr)
2115
 
            if chal is None:
2116
 
                value = '%s realm="%s"' % (scheme.title(), self.transport.host)
2117
 
            else:
2118
 
                value = '%s %s,realm="%s"' % (scheme.title(), chal,
2119
 
                                              self.transport.host)
2120
 
            m.headers.setdefault('www-authenticate', []).append(value)
2121
 
        return m
2122
 
 
2123
 
    
2124
 
 
2125
 
 
2126
 
class Originator:
2127
 
 
2128
 
    def start(self, transport):
2129
 
        self.transport = transport
2130
 
 
2131
 
    def originate(self, fromURL, toURL):        
2132
 
        # Call fromURL, wait for pickup, then call toURL and connect
2133
 
        # them (reinvite?)
2134
 
        pass
2135
 
 
2136
 
class Terminator:
2137
 
 
2138
 
    def start(self, transport):
2139
 
        self.transport = transport
2140