sippy/Rtp_proxy_session.py
class Rtp_proxy_session(object):
    rtp_proxy_client = None
    call_id = None
    from_tag = None
    to_tag = None
    caller_session_exists = False
    caller_codecs = None
    caller_raddress = None
    callee_session_exists = False
    callee_codecs = None
    callee_raddress = None
    max_index = -1
    origin = None
    notify_socket = None
    notify_tag = None
    global_config = None

    def __init__(self, global_config, call_id = None, from_tag = None, to_tag = None,
      notify_socket = None, notify_tag = None):
        self.global_config = global_config
        if global_config.has_key('_rtp_proxy_clients'):
            rtp_proxy_clients = [x for x in global_config['_rtp_proxy_clients'] if x.online]
            n = len(rtp_proxy_clients)
            if n == 0:
                raise Exception('No online RTP proxy client has been found')
            self.rtp_proxy_client = rtp_proxy_clients[int(random() * n)]
        else:
            self.rtp_proxy_client = global_config['rtp_proxy_client']
            if not self.rtp_proxy_client.online:
                raise Exception('No online RTP proxy client has been found')
        if call_id != None:
            self.call_id = call_id
        else:
            self.call_id = md5(str(random()) + str(time())).hexdigest()
        if from_tag != None:
            self.from_tag = from_tag
        else:
            self.from_tag = md5(str(random()) + str(time())).hexdigest()
        if to_tag != None:
            self.to_tag = to_tag
        else:
            self.to_tag = md5(str(random()) + str(time())).hexdigest()
        self.origin = SdpOrigin()
        self.notify_socket = notify_socket
        self.notify_tag = notify_tag

    def version(self, result_callback):
        self.rtp_proxy_client.send_command('V', self.version_result, result_callback)

    def version_result(self, result, result_callback):
        result_callback(result)

    def play_caller(self, prompt_name, times = 1, result_callback = None, index = 0):
        if not self.caller_session_exists:
            return
        if not self.callee_session_exists:
            self.update_callee('0.0.0.0', 0, self._play_caller, '', index, prompt_name, times, result_callback, index)
            return
        self._play_caller(None, prompt_name, times, result_callback, index)

    def _play_caller(self, result, prompt_name, times, result_callback, index):
        command = 'P%d %s %s %s %s %s' % (times, '%s-%d' % (self.call_id, index), prompt_name, self.caller_codecs, self.from_tag, self.to_tag)
        self.rtp_proxy_client.send_command(command, self.command_result, result_callback)

    def play_callee(self, prompt_name, times = 1, result_callback = None, index = 0):
        if not self.callee_session_exists:
            return
        if not self.caller_session_exists:
            self.update_caller('0.0.0.0', 0, self._play_callee, '', index, prompt_name, times, result_callback, index)
            return
        self._play_callee(None, prompt_name, times, result_callback, index)

    def _play_callee(self, result, prompt_name, times, result_callback, index):
        command = 'P%d %s %s %s %s %s' % (times, '%s-%d' % (self.call_id, index), prompt_name, self.callee_codecs, self.to_tag, self.from_tag)
        self.rtp_proxy_client.send_command(command, self.command_result, result_callback)

    def stop_play_caller(self, result_callback = None, index = 0):
        if not self.caller_session_exists:
            return
        command = 'S %s %s %s' % ('%s-%d' % (self.call_id, index), self.from_tag, self.to_tag)
        self.rtp_proxy_client.send_command(command, self.command_result, result_callback)

    def stop_play_callee(self, result_callback = None, index = 0):
        if not self.caller_session_exists:
            return
        command = 'S %s %s %s' % ('%s-%d' % (self.call_id, index), self.to_tag, self.from_tag)
        self.rtp_proxy_client.send_command(command, self.command_result, result_callback)

    def copy_caller(self, remote_ip, remote_port, result_callback = None, index = 0):
        if not self.caller_session_exists:
            self.update_caller('0.0.0.0', 0, self._copy_caller, '', index, remote_ip, remote_port, result_callback, index)
            return
        self._copy_caller(None, remote_ip, remote_port, result_callback, index)

    def _copy_caller(self, result, remote_ip, remote_port, result_callback = None, index = 0):
        command = 'C %s udp:%s:%d %s %s' % ('%s-%d' % (self.call_id, index), remote_ip, remote_port, self.from_tag, self.to_tag)
        self.rtp_proxy_client.send_command(command, self.command_result, result_callback)

    def copy_callee(self, remote_ip, remote_port, result_callback = None, index = 0):
        if not self.callee_session_exists:
            self.update_callee('0.0.0.0', 0, self._copy_callee, '', index, remote_ip, remote_port, result_callback, index)
            return
        self._copy_callee(None, remote_ip, remote_port, result_callback, index)

    def _copy_callee(self, result, remote_ip, remote_port, result_callback = None, index = 0):
        command = 'C %s udp:%s:%d %s %s' % ('%s-%d' % (self.call_id, index), remote_ip, remote_port, self.to_tag, self.from_tag)
        self.rtp_proxy_client.send_command(command, self.command_result, result_callback)

    def start_recording(self, rname = None, result_callback = None, index = 0):
        if not self.caller_session_exists:
            self.update_caller('0.0.0.0', 0, self._start_recording, '', index, rname, result_callback, index)
            return
        self._start_recording(None, rname, result_callback, index)

    def _start_recording(self, result, rname, result_callback, index):
        if rname == None:
            command = 'R %s %s %s' % ('%s-%d' % (self.call_id, index), self.from_tag, self.to_tag)
            return self.rtp_proxy_client.send_command(command, self.command_result, result_callback)
        command = 'C %s %s.a %s %s' % ('%s-%d' % (self.call_id, index), rname, self.from_tag, self.to_tag)
        return self.rtp_proxy_client.send_command(command, self._start_recording1, \
          (rname, result_callback, index))

    def _start_recording1(self, result, args):
        rname, result_callback, index = args
        command = 'C %s %s.o %s %s' % ('%s-%d' % (self.call_id, index), rname, self.to_tag, self.from_tag)
        return self.rtp_proxy_client.send_command(command, self.command_result, result_callback)

    def command_result(self, result, result_callback):
        if result_callback != None:
            result_callback(result)

    def update_caller(self, remote_ip, remote_port, result_callback, options = '', index = 0, *callback_parameters):
        command = 'U'
        self.max_index = max(self.max_index, index)
        if self.rtp_proxy_client.sbind_supported and self.caller_raddress != None:
            if self.rtp_proxy_client.is_local:
                options += 'L%s' % self.global_config['_sip_tm'].l4r.getServer( \
                  self.caller_raddress).laddress[0]
            else:
                options += 'R%s' % self.caller_raddress[0]
        command += options
        command += ' %s %s %d %s' % ('%s-%d' % (self.call_id, index), remote_ip, remote_port, self.from_tag)
        if self.caller_session_exists:
            command += ' %s' % self.to_tag
        if self.notify_socket != None and index == 0 and \
          self.rtp_proxy_client.tnot_supported:
            command += ' %s %s' % (self.notify_socket, self.notify_tag)
        self.rtp_proxy_client.send_command(command, self.update_result, (result_callback, 'caller', callback_parameters))

    def update_callee(self, remote_ip, remote_port, result_callback, options = '', index = 0, *callback_parameters):
        command = 'U'
        self.max_index = max(self.max_index, index)
        if self.rtp_proxy_client.sbind_supported and self.callee_raddress != None:
            if self.rtp_proxy_client.is_local:
                options += 'L%s' % self.global_config['_sip_tm'].l4r.getServer( \
                  self.callee_raddress).laddress[0]
            else:
                options += 'R%s' % self.callee_raddress[0]
        command += options
        command += ' %s %s %d %s %s' % ('%s-%d' % (self.call_id, index), remote_ip, remote_port, self.to_tag, self.from_tag)
        if self.notify_socket != None and index == 0 \
          and self.rtp_proxy_client.tnot_supported:
            command += ' %s %s' % (self.notify_socket, self.notify_tag)
        self.rtp_proxy_client.send_command(command, self.update_result, (result_callback, 'callee', callback_parameters))

    def update_result(self, result, args):
        result_callback, face, callback_parameters = args
        if face == 'caller':
            self.caller_session_exists = True
        else:
            self.callee_session_exists = True
        if result == None:
            result_callback(None, *callback_parameters)
            return
        t1 = result.split()
        rtpproxy_port = int(t1[0])
        if rtpproxy_port == 0:
            result_callback(None, *callback_parameters)
        family = 'IP4'
        if len(t1) > 1:
            rtpproxy_address = t1[1]
            if len(t1) > 2 and t1[2] == '6':
                family = 'IP6'
        else:
            rtpproxy_address = self.rtp_proxy_client.proxy_address
        result_callback((rtpproxy_address, rtpproxy_port, family), *callback_parameters)

    def delete(self):
        while self.max_index >= 0:
            command = 'D %s %s %s' % ('%s-%d' % (self.call_id, self.max_index), self.from_tag, self.to_tag)
            self.rtp_proxy_client.send_command(command)
            self.max_index -= 1

    def on_caller_sdp_change(self, sdp_body, result_callback):
        self.on_xxx_sdp_change(self.update_caller, sdp_body, result_callback)

    def on_callee_sdp_change(self, sdp_body, result_callback):
        self.on_xxx_sdp_change(self.update_callee, sdp_body, result_callback)

    def on_xxx_sdp_change(self, update_xxx, sdp_body, result_callback):
        sects = []
        try:
            sdp_body.parse()
        except Exception, exception:
            print datetime.now(), 'can\'t parse SDP body: %s:' % str(exception)
            print '-' * 70
            print_exc(file = sys.stdout)
            print '-' * 70
            print sdp_body.content
            print '-' * 70
            sys.stdout.flush()
            return
        for i in range(0, len(sdp_body.content.sections)):
            sect = sdp_body.content.sections[i]
            if sect.m_header.transport.lower() not in ('udp', 'udptl', 'rtp/avp'):
                continue
            sects.append(sect)
        if len(sects) == 0:
            sdp_body.needs_update = False
            result_callback(sdp_body)
            return
        formats = sects[0].m_header.formats
        if update_xxx == self.update_caller:
            if len(formats) > 1:
                self.caller_codecs = reduce(lambda x, y: str(x) + ',' + str(y), formats)
            else:
                self.caller_codecs = str(formats[0])
        else:
            if len(formats) > 1:
                self.callee_codecs = reduce(lambda x, y: str(x) + ',' + str(y), formats)
            else:
                self.callee_codecs = str(formats[0])
        for sect in sects:
            options = ''
            if sect.c_header.atype == 'IP6':
                options = '6'
            update_xxx(sect.c_header.addr, sect.m_header.port, self.xxx_sdp_change_finish, options, \
              sects.index(sect), sdp_body, sect, sects, result_callback)
        return

    def xxx_sdp_change_finish(self, address_port, sdp_body, sect, sects, result_callback):
        sect.needs_update = False
        if address_port != None:
            sect.c_header.atype = address_port[2]
            sect.c_header.addr = address_port[0]
            if sect.m_header.port != 0:
                sect.m_header.port = address_port[1]
        if len([x for x in sects if x.needs_update]) == 0:
            sdp_body.content.o_header = self.origin
            sdp_body.needs_update = False
            result_callback(sdp_body)

    def __del__(self):
        self.delete()
        self.rtp_proxy_client = None
sippy/Signal.py
class Signal(object):
    callback = None
    parameters = None
    previous_handler = None

    def __init__(self, signum, callback, *parameters):
        self.callback = callback
        self.parameters = parameters
        self.previous_handler = signal(signum, self.signal_handler)

    def signal_handler(self, signum, *frame):
        try:
            reactor.callFromThread(self.callback, *self.parameters)
        except:
            print datetime.now(), 'Signal: unhandled exception in signal callback'
            print '-' * 70
            print_exc(file = stdout)
            print '-' * 70
            stdout.flush()
        if self.previous_handler not in (SIG_IGN, SIG_DFL):
            try:
                self.previous_handler(signum, *frame)
            except:
                print datetime.now(), 'Signal: unhandled exception in signal chain'
                print '-' * 70
                print_exc(file = stdout)
                print '-' * 70
                stdout.flush()
sippy/SipCiscoGUID.py
class SipCiscoGUID(SipGenericHF):
    hf_names = ('cisco-guid', 'h323-conf-id')
    ciscoGUID = None

    def __init__(self, body = None, ciscoGUID = None):
        SipGenericHF.__init__(self, body)
        if body != None:
            return
        self.parsed = True
        if ciscoGUID != None:
            self.ciscoGUID = ciscoGUID
        else:
            s = md5(str((random() * 1000000000L) + time())).hexdigest()
            self.ciscoGUID = (long(s[0:8], 16), long(s[8:16], 16), long(s[16:24], 16), long(s[24:32], 16))

    def parse(self):
        self.parsed = True
        self.ciscoGUID = tuple([int(x) for x in  self.body.split('-', 3)])

    def __str__(self):
        if not self.parsed:
            return self.body
        return '%d-%d-%d-%d' % self.ciscoGUID

    def getCiscoGUID(self):
        return self.ciscoGUID

    def hexForm(self):
        return '%.8X %.8X %.8X %.8X' % self.ciscoGUID

    def getCanName(self, name, compact = False):
        if name.lower() == 'h323-conf-id':
            return 'h323-conf-id'
        else:
            return 'cisco-GUID'

    def getCopy(self):
        if not self.parsed:
            return SipCiscoGUID(self.body)
        return SipCiscoGUID(ciscoGUID = self.ciscoGUID)
sippy/SipHeader.py
class SipHeader(object):
    name = None
    body = None

    def __init__(self, s = None, name = None, body = None, bodys = None, fixname = False):
        if s != None:
            name, bodys = [x.strip() for x in s.split(':', 1)]
        if name != None:
            self.name = name.lower()
        if body == None:
            try:
                try:
                    body = hf_types[self.name](bodys)
                except KeyError:
                    body = SipGenericHF(bodys, name)
            except ESipHeaderCSV, einst:
                einst.name = self.name
                raise einst
        self.body = body
        # If no name is provided use canonic name from the body-specific
        # class.
        if self.name == None or fixname:
            self.name = body.hf_names[0]

    def __str__(self):
        return str(self.body.getCanName(self.name)) + ': ' + str(self.body)

    def localStr(self, local_addr = None, local_port = None, compact = False):
        return str(self.body.getCanName(self.name, compact)) + ': ' + \
          self.body.localStr(local_addr, local_port)

    def getBody(self):
        if not self.body.parsed:
            self.body.parse()
        return self.body
sippy/SipFrom.py
class SipFrom(SipAddressHF):
    hf_names = ('from', 'f')

    def __init__(self, body = None, address = None):
        SipAddressHF.__init__(self, body, address)
        if body == None and address == None:
            self.address = SipAddress(name = 'Anonymous', url = SipURL(host = SipConf.my_address, port = SipConf.my_port))

    def getTag(self):
        return self.address.getParam('tag')

    def genTag(self):
        self.address.setParam('tag', md5(str((random() * 1000000000L) + time())).hexdigest())

    def setTag(self, value):
        self.address.setParam('tag', value)

    def delTag(self):
        self.address.delParam('tag')

    def getCanName(self, name, compact = False):
        if compact:
            return 'f'
        return 'From'
sippy/CCEvents.py
class CCEventDisconnect(CCEventGeneric):
    name = 'CCEventDisconnect'
    pass
sippy/UA.py
class UA(object):
    global_config = None
    state = None
    event_cb = None
    uasReq = None
    uacResp = None
    username = None
    password = None
    equeue = None
    dId = None
    credit_time = None
    credit_times = None
    credit_timer = None
    conn_cbs = None
    disc_cbs = None
    fail_cbs = None
    ring_cbs = None
    dead_cbs = None
    rCSeq = None
    lTag = None
    lUri = None
    rUri = None
    cId = None
    lCSeq = None
    lContact = None
    cGUID = None
    rAddr = None
    rAddr0 = None
    routes = None
    rTarget = None
    uasResp = None
    lSDP = None
    rSDP = None
    kaInterval = 0
    branch = None
    reqs = None
    extra_headers = None
    useRefer = True
    expire_time = None
    expire_timer = None
    no_progress_time = None
    no_progress_timer = None
    no_reply_time = None
    no_reply_timer = None
    on_local_sdp_change = None
    on_remote_sdp_change = None
    last_scode = 100
    setup_ts = None
    p100_ts = None
    p1xx_ts = None
    connect_ts = None
    disconnect_ts = None
    user_agent = None
    elast_seq = None
    origin = None
    source_address = None

    def __init__(self, global_config, event_cb = None, username = None, password = None, nh_address = None, credit_time = None, \
      conn_cbs = None, disc_cbs = None, fail_cbs = None, ring_cbs = None, dead_cbs = None, ltag = None, extra_headers = None, \
      expire_time = None, no_progress_time = None):
        self.global_config = global_config
        self.event_cb = event_cb
        self.equeue = []
        self.username = username
        self.password = password
        self.rAddr = nh_address
        self.rAddr0 = self.rAddr
        self.credit_time = credit_time
        self.credit_times = {}
        if conn_cbs != None:
            self.conn_cbs = conn_cbs
        else:
            self.conn_cbs = ()
        if disc_cbs != None:
            self.disc_cbs = disc_cbs
        else:
            self.disc_cbs = ()
        if fail_cbs != None:
            self.fail_cbs = fail_cbs
        else:
            self.fail_cbs = ()
        if ring_cbs != None:
            self.ring_cbs = ring_cbs
        else:
            self.ring_cbs = ()
        if dead_cbs != None:
            self.dead_cbs = dead_cbs
        else:
            self.dead_cbs = ()
        if ltag != None:
            self.lTag = ltag
        else:
            self.lTag = md5(str((random() * 1000000000L) + time())).hexdigest()
        self.reqs = {}
        self.extra_headers = extra_headers
        self.expire_time = expire_time
        self.no_progress_time = no_progress_time
        #print self.username, self.password

    def recvRequest(self, req):
        #print 'Received request %s in state %s instance %s' % (req.getMethod(), self.state, self)
        #print self.rCSeq, req.getHFBody('cseq').getCSeqNum()
        if self.user_agent == None:
            self.update_ua(req)
        if self.rCSeq != None and self.rCSeq >= req.getHFBody('cseq').getCSeqNum():
            return (req.genResponse(500, 'Server Internal Error'), None, None)
        self.rCSeq = req.getHFBody('cseq').getCSeqNum()
        if self.state == None:
            if req.getMethod() == 'INVITE':
                self.changeState((UasStateIdle,))
            else:
                return None
        newstate = self.state.recvRequest(req)
        if newstate != None:
            self.changeState(newstate)
        self.emitPendingEvents()
        if newstate != None and req.getMethod() == 'INVITE':
            return (None, self.state.cancel, self.disconnect)
        else:
            return None

    def recvResponse(self, resp):
        if self.state == None:
            return
        self.update_ua(resp)
        code, reason = resp.getSCode()
        cseq, method = resp.getHFBody('cseq').getCSeq()
        if method == 'INVITE' and self.reqs.has_key(cseq) and code == 401 and resp.countHFs('www-authenticate') != 0 and \
          self.username != None and self.password != None and self.reqs[cseq].countHFs('authorization') == 0:
            challenge = resp.getHFBody('www-authenticate')
            req = self.genRequest('INVITE', self.lSDP, challenge.getNonce(), challenge.getRealm())
            self.lCSeq += 1
            self.tr = self.global_config['_sip_tm'].newTransaction(req, self.recvResponse, \
              laddress = self.source_address)
            del self.reqs[cseq]
            return None
        if method == 'INVITE' and self.reqs.has_key(cseq) and code == 407 and resp.countHFs('proxy-authenticate') != 0 and \
          self.username != None and self.password != None and self.reqs[cseq].countHFs('proxy-authorization') == 0:
            challenge = resp.getHFBody('proxy-authenticate')
            req = self.genRequest('INVITE', self.lSDP, challenge.getNonce(), challenge.getRealm(), SipProxyAuthorization)
            self.lCSeq += 1
            self.tr = self.global_config['_sip_tm'].newTransaction(req, self.recvResponse, \
              laddress = self.source_address)
            del self.reqs[cseq]
            return None
        if code >= 200 and self.reqs.has_key(cseq):
            del self.reqs[cseq]
        newstate = self.state.recvResponse(resp)
        if newstate != None:
            self.changeState(newstate)
        self.emitPendingEvents()

    def recvEvent(self, event):
        #print self, event
        if self.state == None:
            if isinstance(event, CCEventTry) or isinstance(event, CCEventFail) or isinstance(event, CCEventDisconnect):
                self.changeState((UacStateIdle,))
            else:
                return
        newstate = self.state.recvEvent(event)
        if newstate != None:
            self.changeState(newstate)
        self.emitPendingEvents()

    def disconnect(self, rtime = None):
        if rtime == None:
            rtime = time()
        self.equeue.append(CCEventDisconnect(rtime = rtime))
        self.recvEvent(CCEventDisconnect(rtime = rtime))

    def expires(self):
        self.expire_timer = None
        self.disconnect()

    def no_progress_expires(self):
        self.no_progress_timer = None
        self.disconnect()

    def no_reply_expires(self):
        self.no_reply_timer = None
        self.disconnect()

    def credit_expires(self, rtime):
        self.credit_timer = None
        self.disconnect(rtime)

    def changeState(self, newstate):
        if self.state != None:
            self.state.onStateChange(newstate[0])
        self.state = newstate[0](self)
        if len(newstate) > 1:
            for callback in newstate[1]:
                callback(self, *newstate[2:])

    def emitEvent(self, event):
        if self.event_cb != None:
            if self.elast_seq != None and self.elast_seq >= event.seq:
                #print 'ignoring out-of-order event', event, event.seq, self.elast_seq, self.cId
                return
            self.elast_seq = event.seq
            self.event_cb(event, self)

    def emitPendingEvents(self):
        while len(self.equeue) != 0 and self.event_cb != None:
            event = self.equeue.pop(0)
            if self.elast_seq != None and self.elast_seq >= event.seq:
                #print 'ignoring out-of-order event', event, event.seq, self.elast_seq, self.cId
                continue
            self.elast_seq = event.seq
            self.event_cb(event, self)

    def genRequest(self, method, body = None, nonce = None, realm = None, SipXXXAuthorization = SipAuthorization, \
      reason = None):
        req = SipRequest(method = method, ruri = self.rTarget, to = self.rUri, fr0m = self.lUri,
                         cseq = self.lCSeq, callid = self.cId, contact = self.lContact,
                         routes = self.routes, target = self.rAddr, cguid = self.cGUID)
        if nonce != None and realm != None and self.username != None and self.password != None:
            auth = SipXXXAuthorization(realm = realm, nonce = nonce, method = method, uri = str(self.rTarget),
              username = self.username, password = self.password)
            req.appendHeader(SipHeader(body = auth))
        if body != None:
            req.setBody(body)
        if self.extra_headers != None:
            req.appendHeaders(self.extra_headers)
        if reason != None:
            req.appendHeader(SipHeader(body = reason))
        self.reqs[self.lCSeq] = req
        return req

    def sendUasResponse(self, scode, reason, body = None, contact = None, \
      reason_rfc3326 = None, extra_header = None):
        self.uasResp.setSCode(scode, reason)
        self.uasResp.setBody(body)
        self.uasResp.delHFs('www-authenticate')
        self.uasResp.delHFs('contact')
        self.uasResp.delHFs('reason')
        if contact != None:
            self.uasResp.appendHeader(SipHeader(name = 'contact', body = contact))
        if reason_rfc3326 != None:
            self.uasResp.appendHeader(SipHeader(body = reason_rfc3326))
        if extra_header != None:
            self.uasResp.appendHeader(extra_header)
        self.global_config['_sip_tm'].sendResponse(self.uasResp)

    def isYours(self, req = None, call_id = None, from_tag = None, to_tag = None):
        #print self.branch, req.getHFBody('via').getBranch()
        if req != None:
            if req.getMethod() != 'BYE' and self.branch != None and \
              self.branch != req.getHFBody('via').getBranch():
                return None
            call_id = str(req.getHFBody('call-id'))
            from_tag = req.getHFBody('from').getTag()
            to_tag = req.getHFBody('to').getTag()
        #print str(self.cId), call_id
        if call_id != str(self.cId):
            return None
        #print self.rUri.getTag(), from_tag
        if self.rUri != None and self.rUri.getTag() != from_tag:
            return None
        #print self.lUri.getTag(), to_tag
        if self.lUri != None and self.lUri.getTag() != to_tag:
            return None
        return self

    def isDead(self):
        if self.state != None:
            return self.state.dead
        return False

    def isConnected(self):
        if self.state != None:
            return self.state.connected
        return False

    def getCLD(self):
        if self.rUri == None:
            return None
        return self.rUri.getUrl().username

    def getCLI(self):
        if self.lUri == None:
            return None
        return self.lUri.getUrl().username

    def getCallingName(self):
        if self.lUri == None:
            return None
        return self.lUri.getUri().name

    def getRAddr0(self):
        return self.rAddr0

    def getCID(self):
        # Return tuple containing call-id, local tag and remote tag
        rval = [str(self.cId), None, None]
        if self.lUri != None:
            rval[1] = self.lUri.getTag()
        if self.rUri != None:
            rval[2] = self.rUri.getTag()
        return tuple(rval)

    def delayed_remote_sdp_update(self, event, remote_sdp_body):
        self.rSDP = remote_sdp_body.getCopy()
        self.equeue.append(event)
        self.emitPendingEvents()

    def getAcct(self):
        if self.disconnect_ts != None:
            disconnect_ts = self.disconnect_ts
            disconnected = True
        else:
            disconnect_ts = time()
            disconnected = False
        if self.connect_ts != None:
            return (disconnect_ts - self.connect_ts, self.connect_ts - self.setup_ts, True, disconnected)
        return (0, disconnect_ts - self.setup_ts, False, disconnected)

    def update_ua(self, msg):
        if msg.countHFs('user-agent') > 0:
            self.user_agent = msg.getHFBody('user-agent').name
        elif msg.countHFs('server') > 0:
            self.user_agent = msg.getHFBody('server').name
        return

    def cancelCreditTimer(self):
        if self.credit_timer != None:
            self.credit_timer.cancel()
            self.credit_timer = None

    def startCreditTimer(self, rtime):
        if self.credit_time != None:
            self.credit_times[0] = rtime + self.credit_time
            self.credit_time = None
        try:
            credit_time = min([x for x in self.credit_times.values() if x != None])
        except ValueError:
            return
        self.credit_timer = TimeoutAbs(self.credit_expires, credit_time, credit_time)

    def resetCreditTime(self, rtime, new_credit_times):
        self.credit_times.update(new_credit_times)
        if self.state.connected:
            self.cancelCreditTimer()
            self.startCreditTimer(rtime)

    def cleanup(self):
        pass
sippy/CCEvents.py
class CCEventTry(CCEventGeneric):
    name = 'CCEventTry'
    pass
sippy/RadiusAuthorisation.py
class RadiusAuthorisation(Radius_client):
    def do_auth(self, username, caller, callee, h323_cid, sip_cid, remote_ip, res_cb, \
      realm = None, nonce = None, uri = None, response = None, extra_attributes = None):
        sip_cid = str(sip_cid)
        attributes = None
        if None not in (realm, nonce, uri, response):
            attributes = [('User-Name', username), ('Digest-Realm', realm), \
              ('Digest-Nonce', nonce), ('Digest-Method', 'INVITE'), ('Digest-URI', uri), \
              ('Digest-Algorithm', 'MD5'), ('Digest-User-Name', username), ('Digest-Response', response)]
        else:
            attributes = [('User-Name', remote_ip), ('Password', 'cisco')]
        if caller == None:
            caller = ''
        attributes.extend((('Calling-Station-Id', caller), ('Called-Station-Id', callee), ('h323-conf-id', h323_cid), \
          ('call-id', sip_cid), ('h323-remote-address', remote_ip), ('h323-session-protocol', 'sipv2')))
        if extra_attributes != None:
            for a, v in extra_attributes:
                attributes.append((a, v))
        message = 'sending AAA request:\n' 
        message += reduce(lambda x, y: x + y, ['%-32s = \'%s\'\n' % (x[0], str(x[1])) for x in attributes])
        self.global_config['_sip_logger'].write(message, call_id = sip_cid)
        Radius_client.do_auth(self, attributes, self._process_result, res_cb, sip_cid, time())

    def _process_result(self, results, res_cb, sip_cid, btime):
        delay = time() - btime
        rcode = results[1]
        if rcode in (0, 1):
            if rcode == 0:
                message = 'AAA request accepted (delay is %.3f), processing response:\n' % delay
            else:
                message = 'AAA request rejected (delay is %.3f), processing response:\n' % delay
            if len(results[0]) > 0:
                message += reduce(lambda x, y: x + y, ['%-32s = \'%s\'\n' % x for x in results[0]])
        else:
            message = 'Error sending AAA request (delay is %.3f)\n' % delay
        self.global_config['_sip_logger'].write(message, call_id = sip_cid)
        res_cb(results)
sippy/CCEvents.py
class CCEventConnect(CCEventGeneric):
    name = 'CCEventConnect'
    pass
sippy/UasStateTrying.py
class UasStateTrying(UaStateGeneric):
    sname = 'Trying(UAS)'

    def recvEvent(self, event):
        if isinstance(event, CCEventRing):
            scode = event.getData()
            if scode == None:
                code, reason, body = (180, 'Ringing', None)
            else:
                code, reason, body = scode
                if code == 100:
                    return None
                if body != None and self.ua.on_local_sdp_change != None and body.needs_update:
                    self.ua.on_local_sdp_change(body, lambda x: self.ua.recvEvent(event))
                    return None
            self.ua.lSDP = body
            self.ua.sendUasResponse(code, reason, body)
            if self.ua.no_progress_timer != None:
                self.ua.no_progress_timer.cancel()
                self.ua.no_progress_timer = None
                if self.ua.expire_time != None:
                    self.ua.expire_timer = TimeoutAbs(self.ua.expires, self.ua.expire_time)
            if self.ua.p1xx_ts == None:
                self.ua.p1xx_ts = event.rtime
            return (UasStateRinging, self.ua.ring_cbs, event.rtime, event.origin, code)
        elif isinstance(event, CCEventConnect):
            code, reason, body = event.getData()
            if body != None and self.ua.on_local_sdp_change != None and body.needs_update:
                self.ua.on_local_sdp_change(body, lambda x: self.ua.recvEvent(event))
                return None
            self.ua.lSDP = body
            self.ua.sendUasResponse(code, reason, body, self.ua.lContact)
            if self.ua.expire_timer != None:
                self.ua.expire_timer.cancel()
                self.ua.expire_timer = None
            if self.ua.no_progress_timer != None:
                self.ua.no_progress_timer.cancel()
                self.ua.no_progress_timer = None
            self.ua.startCreditTimer(event.rtime)
            self.ua.connect_ts = event.rtime
            return (UaStateConnected, self.ua.conn_cbs, event.rtime, event.origin)
        elif isinstance(event, CCEventRedirect):
            scode = event.getData()
            if scode == None:
                scode = (500, 'Failed', None, None)
            self.ua.sendUasResponse(scode[0], scode[1], scode[2], SipContact(address = SipAddress(url = scode[3])))
            if self.ua.expire_timer != None:
                self.ua.expire_timer.cancel()
                self.ua.expire_timer = None
            if self.ua.no_progress_timer != None:
                self.ua.no_progress_timer.cancel()
                self.ua.no_progress_timer = None
            self.ua.disconnect_ts = event.rtime
            return (UaStateFailed, self.ua.fail_cbs, event.rtime, event.origin, scode[0])
        elif isinstance(event, CCEventFail):
            scode = event.getData()
            if scode == None:
                scode = (500, 'Failed')
            self.ua.sendUasResponse(scode[0], scode[1], reason_rfc3326 = event.reason, \
              extra_header = event.extra_header)
            if self.ua.expire_timer != None:
                self.ua.expire_timer.cancel()
                self.ua.expire_timer = None
            if self.ua.no_progress_timer != None:
                self.ua.no_progress_timer.cancel()
                self.ua.no_progress_timer = None
            self.ua.disconnect_ts = event.rtime
            return (UaStateFailed, self.ua.fail_cbs, event.rtime, event.origin, scode[0])
        elif isinstance(event, CCEventDisconnect):
            #import sys, traceback
            #traceback.print_stack(file = sys.stdout)
            self.ua.sendUasResponse(500, 'Disconnected', reason_rfc3326 = event.reason)
            if self.ua.expire_timer != None:
                self.ua.expire_timer.cancel()
                self.ua.expire_timer = None
            if self.ua.no_progress_timer != None:
                self.ua.no_progress_timer.cancel()
                self.ua.no_progress_timer = None
            self.ua.disconnect_ts = event.rtime
            return (UaStateDisconnected, self.ua.disc_cbs, event.rtime, event.origin, self.ua.last_scode)
        #print 'wrong event %s in the Trying state' % event
        return None

    def cancel(self, rtime, req):
        self.ua.disconnect_ts = rtime
        self.ua.changeState((UaStateDisconnected, self.ua.disc_cbs, rtime, self.ua.origin))
        event = CCEventDisconnect(rtime = rtime, origin = self.ua.origin)
        if req != None:
            try:
                event.reason = req.getHFBody('reason')
            except:
                pass
        self.ua.emitEvent(event)
sippy/SipTo.py
class SipTo(SipFrom):
    hf_names = ('to', 't')

    def getCanName(self, name, compact = False):
        if compact:
            return 't'
        return 'To'
sippy/CCEvents.py
class CCEventUpdate(CCEventGeneric):
    name = 'CCEventUpdate'
    pass
sippy/SipLogger.py
class SipLogger(object):
    app = None
    call_id = None
    log = None
    level = None
    flock = lambda x, y, z: None

    def __init__(self, app, call_id = 'GLOBAL', logfile = '/var/log/sip.log'):
        self.app = app
        self.call_id = call_id
        bend = os.environ.get('SIPLOG_BEND', 'stderr').lower()
        if bend == 'stderr':
            self.log = sys.__stderr__
        elif bend == 'none':
            self.write = self.donoting
        else:
            logfile = os.environ.get('SIPLOG_LOGFILE_FILE', logfile)
            self.log = file(logfile, 'a')
            self.flock = flock
            Signal(SIGUSR1, self.reopen, logfile)
        self.level = eval('SIPLOG_' + os.environ.get('SIPLOG_LVL', 'INFO'))

    def donoting(self, *args, **kwargs):
        pass

    def write(self, *args, **kwargs):
        if kwargs.get('level', SIPLOG_INFO) < self.level:
            return
        ltime = kwargs.get('ltime', None)
        if ltime == None:
            ltime = time()
        call_id = kwargs.get('call_id', self.call_id)
        obuf = '%s.%.3d/%s/%s: %s\n' % (strftime('%d %b %H:%M:%S', localtime(ltime)), \
          (ltime % 1) * 1000, call_id, self.app, \
          reduce(lambda x, y: x + y, [str(x) for x in args]))
        try:
            self.flock(self.log, LOCK_EX)
        except IOError, e:
            # Catch ENOTSUP
            if e.args[0] != 45:
                raise e
            self.flock = lambda x, y: None
        try:
            self.log.write(obuf)
        except IOError, e:
            if e.args[0] != EINTR:
                raise e
        self.log.flush()
        self.flock(self.log, LOCK_UN)

    def reopen(self, logfile):
        self.log = file(logfile, 'a')
sippy/CCEvents.py
class CCEventFail(CCEventGeneric):
    name = 'CCEventFail'
    pass
sippy/UasStateRinging.py
class UasStateRinging(UaStateGeneric):
    sname = 'Ringing(UAS)'

    def recvEvent(self, event):
        if isinstance(event, CCEventRing):
            scode = event.getData()
            if scode == None:
                code, reason, body = (180, 'Ringing', None)
            else:
                code, reason, body = scode
                if code == 100:
                    return None
                if body != None and self.ua.on_local_sdp_change != None and body.needs_update:
                    self.ua.on_local_sdp_change(body, lambda x: self.ua.recvEvent(event))
                    return None
            self.ua.lSDP = body
            if self.ua.p1xx_ts == None:
                self.ua.p1xx_ts = event.rtime
            self.ua.sendUasResponse(code, reason, body)
            for ring_cb in self.ua.ring_cbs:
                ring_cb(self.ua, event.rtime, event.origin, code)
            return None
        elif isinstance(event, CCEventConnect):
            code, reason, body = event.getData()
            if body != None and self.ua.on_local_sdp_change != None and body.needs_update:
                self.ua.on_local_sdp_change(body, lambda x: self.ua.recvEvent(event))
                return None
            self.ua.lSDP = body
            self.ua.sendUasResponse(code, reason, body, self.ua.lContact)
            if self.ua.expire_timer != None:
                self.ua.expire_timer.cancel()
                self.ua.expire_timer = None
            self.ua.startCreditTimer(event.rtime)
            self.ua.connect_ts = event.rtime
            return (UaStateConnected, self.ua.conn_cbs, event.rtime, event.origin)
        elif isinstance(event, CCEventRedirect):
            scode = event.getData()
            if scode == None:
                scode = (500, 'Failed', None, None)
            self.ua.sendUasResponse(scode[0], scode[1], scode[2], SipContact(address = SipAddress(url = scode[3])))
            if self.ua.expire_timer != None:
                self.ua.expire_timer.cancel()
                self.ua.expire_timer = None
            self.ua.disconnect_ts = event.rtime
            return (UaStateFailed, self.ua.fail_cbs, event.rtime, event.origin, scode[0])
        elif isinstance(event, CCEventFail):
            scode = event.getData()
            if scode == None:
                scode = (500, 'Failed')
            self.ua.sendUasResponse(scode[0], scode[1], reason_rfc3326 = event.reason, \
              extra_header = event.extra_header)
            if self.ua.expire_timer != None:
                self.ua.expire_timer.cancel()
                self.ua.expire_timer = None
            self.ua.disconnect_ts = event.rtime
            return (UaStateFailed, self.ua.fail_cbs, event.rtime, event.origin, scode[0])
        elif isinstance(event, CCEventDisconnect):
            #import sys, traceback
            #traceback.print_stack(file = sys.stdout)
            self.ua.sendUasResponse(500, 'Disconnected', reason_rfc3326 = event.reason)
            if self.ua.expire_timer != None:
                self.ua.expire_timer.cancel()
                self.ua.expire_timer = None
            self.ua.disconnect_ts = event.rtime
            return (UaStateDisconnected, self.ua.disc_cbs, event.rtime, event.origin, self.ua.last_scode)
        #print 'wrong event %s in the Ringing state' % event
        return None

    def recvRequest(self, req):
        if req.getMethod() == 'BYE':
            self.ua.sendUasResponse(487, 'Request Terminated')
            self.ua.global_config['_sip_tm'].sendResponse(req.genResponse(200, 'OK'))
            #print 'BYE received in the Ringing state, going to the Disconnected state'
            if req.countHFs('also') > 0:
                also = req.getHFBody('also').getUrl().getCopy()
            else:
                also = None
            event = CCEventDisconnect(also, rtime = req.rtime, origin = self.ua.origin)
            try:
                event.reason = req.getHFBody('reason')
            except:
                pass
            self.ua.equeue.append(event)
            if self.ua.expire_timer != None:
                self.ua.expire_timer.cancel()
                self.ua.expire_timer = None
            self.ua.disconnect_ts = req.rtime
            return (UaStateDisconnected, self.ua.disc_cbs, req.rtime, self.ua.origin)
        return None

    def cancel(self, rtime, req):
        self.ua.disconnect_ts = rtime
        self.ua.changeState((UaStateDisconnected, self.ua.disc_cbs, rtime, self.ua.origin))
        event = CCEventDisconnect(rtime = rtime, origin = self.ua.origin)
        if req != None:
            try:
                event.reason = req.getHFBody('reason')
            except:
                pass
        self.ua.emitEvent(event)
sippy/RadiusAccounting.py
class RadiusAccounting(object):
    global_config = None
    drec = None
    crec = None
    iTime = None
    cTime = None
    sip_cid = None
    origin = None
    lperiod = None
    el = None
    send_start = None
    complete = False
    ms_precision = False
    user_agent = None
    p1xx_ts = None
    p100_ts = None

    def __init__(self, global_config, origin, lperiod = None, send_start = False):
        self.global_config = global_config
        self._attributes = [('h323-call-origin', origin), ('h323-call-type', 'VoIP'), \
          ('h323-session-protocol', 'sipv2')]
        self.drec = False
        self.crec = False
        self.origin = origin
        self.lperiod = lperiod
        self.send_start = send_start

    def setParams(self, username, caller, callee, h323_cid, sip_cid, remote_ip, \
      h323_in_cid = None):
        if caller == None:
            caller = ''
        self._attributes.extend((('User-Name', username), ('Calling-Station-Id', caller), \
          ('Called-Station-Id', callee), ('h323-conf-id', h323_cid), ('call-id', sip_cid), \
          ('Acct-Session-Id', sip_cid), ('h323-remote-address', remote_ip)))
        if h323_in_cid != None and h323_in_cid != h323_cid:
            self._attributes.append(('h323-incoming-conf-id', h323_in_cid))
        self.sip_cid = str(sip_cid)
        self.complete = True

    def conn(self, ua, rtime, origin):
        if self.crec:
            return
        self.crec = True
        self.iTime = ua.setup_ts
        self.cTime = ua.connect_ts
        if ua.user_agent != None and self.user_agent == None:
            self.user_agent = ua.user_agent
        if ua.p1xx_ts != None:
            self.p1xx_ts = ua.p1xx_ts
        if ua.p100_ts != None:
            self.p100_ts = ua.p100_ts
        if self.send_start:
            self.asend('Start', rtime, origin, ua)
        self._attributes.extend((('h323-voice-quality', 0), ('Acct-Terminate-Cause', 'User-Request')))
        if self.lperiod != None and self.lperiod > 0:
            self.el = Timeout(self.asend, self.lperiod, -1, 'Alive')

    def disc(self, ua, rtime, origin, result = 0):
        if self.drec:
            return
        self.drec = True
        if self.el != None:
            self.el.cancel()
            self.el = None
        if self.iTime == None:
            self.iTime = ua.setup_ts
        if self.cTime == None:
            self.cTime = rtime
        if ua.user_agent != None and self.user_agent == None:
            self.user_agent = ua.user_agent
        if ua.p1xx_ts != None:
            self.p1xx_ts = ua.p1xx_ts
        if ua.p100_ts != None:
            self.p100_ts = ua.p100_ts
        self.asend('Stop', rtime, origin, result, ua)

    def asend(self, type, rtime = None, origin = None, result = 0, ua = None):
        if not self.complete:
            return
        if rtime == None:
            rtime = time()
        if ua != None:
            duration, delay, connected = ua.getAcct()[:3]
        else:
            # Alive accounting
            duration = rtime - self.cTime
            delay = self.cTime - self.iTime
            connected = True
        if not(self.ms_precision):
            duration = round(duration)
            delay = round(delay)
        attributes = self._attributes[:]
        if type != 'Start':
            if result >= 400:
                try:
                    dc = sipErrToH323Err[result][0]
                except:
                    dc = '7f'
            elif result < 200:
                dc = '10'
            else:
                dc = '0'
            attributes.extend((('h323-disconnect-time', self.ftime(self.iTime + delay + duration)), \
              ('Acct-Session-Time', '%d' % round(duration)), ('h323-disconnect-cause', dc)))
        if type == 'Stop':
            if origin == 'caller':
                release_source = '2'
            elif origin == 'callee':
                release_source = '4'
            else:
                release_source = '8'
            attributes.append(('release-source', release_source))
        attributes.extend((('h323-connect-time', self.ftime(self.iTime + delay)), ('h323-setup-time', self.ftime(self.iTime)), \
          ('Acct-Status-Type', type)))
        if self.user_agent != None:
            attributes.append(('h323-ivr-out', 'sip_ua:' + self.user_agent))
        if self.p1xx_ts != None:
            attributes.append(('Acct-Delay-Time', round(self.p1xx_ts)))
        if self.p100_ts != None:
            attributes.append(('provisional-timepoint', self.ftime(self.p100_ts)))
        pattributes = ['%-32s = \'%s\'\n' % (x[0], str(x[1])) for x in attributes]
        pattributes.insert(0, 'sending Acct %s (%s):\n' % (type, self.origin.capitalize()))
        self.global_config['_sip_logger'].write(call_id = self.sip_cid, *pattributes)
        self.global_config['_radius_client'].do_acct(attributes, self._process_result, self.sip_cid, time())

    def ftime(self, t):
        gt = gmtime(t)
        day = strftime('%d', gt)
        if day[0] == '0':
            day = day[1]
        if self.ms_precision:
            msec = (t % 1) * 1000
        else:
            msec = 0
        return strftime('%%H:%%M:%%S.%.3d GMT %%a %%b %s %%Y' % (msec, day), gt)

    def _process_result(self, results, sip_cid, btime):
        delay = time() - btime
        rcode = results[1]
        if rcode in (0, 1):
            if rcode == 0:
                message = 'Acct/%s request accepted (delay is %.3f)\n' % (self.origin, delay)
            else:
                message = 'Acct/%s request rejected (delay is %.3f)\n' % (self.origin, delay)
        else:
            message = 'Error sending Acct/%s request (delay is %.3f)\n' % (self.origin, delay)
        self.global_config['_sip_logger'].write(message, call_id = sip_cid)
sippy/MyConfigParser.py
class MyConfigParser(RawConfigParser):
    default_section = None
    _private_keys = None

    def __init__(self, default_section = 'general'):
        self.default_section = default_section
        self._private_keys = {}
        RawConfigParser.__init__(self)
        self.add_section(self.default_section)

    def __getitem__(self, key):
        if key.startswith('_'):
            return self._private_keys[key]
        value_type  = SUPPORTED_OPTIONS[key][0]
        if value_type  == 'B':
            return self.getboolean(self.default_section, key)
        elif value_type == 'I':
            return self.getint(self.default_section, key)
        return self.get(self.default_section, key)

    def __setitem__(self, key, value):
        if key.startswith('_'):
            self._private_keys[key] = value
        else:
            self.set(self.default_section, key, str(value))
        return

    def has_key(self, key):
        return self.__contains__(key)

    def __contains__(self, key):
        if key.startswith('_'):
            return self._private_keys.has_key(key)
        return self.has_option(self.default_section, key)

    def get(self, *args):
        if len(args) == 1:
            return self.__getitem__(args[0])
        return RawConfigParser.get(self, *args)

    def getdefault(self, key, default_value):
        if self.__contains__(key):
            return self.__getitem__(key)
        return default_value

    def get_longopts(self):
        return tuple([x + '=' for x in SUPPORTED_OPTIONS.keys()])

    def read(self, fname):
        RawConfigParser.readfp(self, open(fname))
        for key in tuple(self.options(self.default_section)):
            self.check_and_set(key, RawConfigParser.get(self, \
              self.default_section, key), False)

    def check_and_set(self, key, value, compat = True):
        value = value.strip()
        if compat:
            if key == 'rtp_proxy_client':
                # XXX compatibility option
                if self.has_key('_rtp_proxy_clients'):
                    self['_rtp_proxy_clients'].append(value)
                else:
                    self['_rtp_proxy_clients'] = [value,]
                if self.has_key('rtp_proxy_clients'):
                    self['rtp_proxy_clients'] += ',' + value
                else:
                    self['rtp_proxy_clients'] = value
                return
            elif key == 'pass_header':
                # XXX compatibility option
                if self.has_key('_pass_headers'):
                    self['_pass_headers'].append(value)
                else:
                    self['_pass_headers'] = [value,]
                if self.has_key('pass_headers'):
                    self['pass_headers'] += ',' + value
                else:
                    self['pass_headers'] = value
                return

        value_type  = SUPPORTED_OPTIONS[key][0]
        if value_type == 'B':
            if value.lower() not in self._boolean_states:
                raise ValueError, 'Not a boolean: %s' % value
        elif value_type == 'I':
            _value = int(value)
        if key in ('keepalive_ans', 'keepalive_orig'):
            if _value < 0:
                raise ValueError, 'keepalive_ans should be non-negative'
        elif key == 'max_credit_time':
            if _value <= 0:
                raise ValueError, 'max_credit_time should be more than zero'
        elif key == 'allowed_pts':
            self['_allowed_pts'] = [int(x) for x in value.split(',')]
        elif key in ('accept_ips', 'pass_headers', 'rtp_proxy_clients'):
            self['_' + key] = [x.strip() for x in value.split(',')]
        elif key == 'sip_address':
            if 'my' in dir(value):
                self['_sip_address'] = value
                value = '*'
            elif value in ('*', '0.0.0.0', '::'):
                self['_sip_address'] = SipConf.my_address
            else:
                self['_sip_address'] = value
        elif key == 'sip_port':
            if _value <= 0 or _value > 65535:
                raise ValueError, 'sip_port should be in the range 1-65535'
            self['_sip_port'] = _value
        self[key] = value

    def options_help(self):
        supported_options = SUPPORTED_OPTIONS.items()
        supported_options.sort()
        for option, (value_type, helptext) in supported_options:
            if value_type == 'B':
                value = 'on/off'
            elif value_type == 'I':
                value = 'number'
            else:
                value = '"string"'
            print '--%s=%s\n\t%s\n' % (option, value, helptext)
sippy/UaStateDead.py
class UaStateDead(UaStateGeneric):
    sname = 'Dead'
    dead = True

    def __init__(self, ua):
        UaStateGeneric.__init__(self, None)
        if ua.cId != None:
            ua.global_config['_sip_tm'].unregConsumer(ua, str(ua.cId))
        ua.tr = None
        ua.event_cb = None
        ua.conn_cbs = ()
        ua.disc_cbs = ()
        ua.fail_cbs = ()
        ua.on_local_sdp_change = None
        ua.on_remote_sdp_change = None
        ua.expire_timer = None
        ua.no_progress_timer = None
        ua.credit_timer = None
        # Keep this at the very end of processing
        for callback in ua.dead_cbs:
            callback(ua)
        ua.dead_cbs = ()
        ua.cleanup()
        # Break cross-ref chain
        self.ua = None
sippy/SipTransactionManager.py
class SipTransactionManager(object):
    global_config = None
    l4r = None
    tclient = None
    tserver = None
    req_cb = None
    l1rcache = None
    l2rcache = None
    nat_traversal = False
    req_consumers = None
    provisional_retr = 0

    def __init__(self, global_config, req_cb = None):
        self.global_config = global_config
        self.l4r = local4remote(global_config, self.handleIncoming)
        self.tclient = {}
        self.tserver = {}
        self.req_cb = req_cb
        self.l1rcache = {}
        self.l2rcache = {}
        self.req_consumers = {}
        Timeout(self.rCachePurge, 32, -1)

    def handleIncoming(self, data, address, server):
        if len(data) < 32:
            return
        rtime = time()
        self.global_config['_sip_logger'].write('RECEIVED message from %s:%d:\n' % address, data, ltime = rtime)
        checksum = md5(data).digest()
        retrans = self.l1rcache.get(checksum, None)
        if retrans == None:
            retrans = self.l2rcache.get(checksum, None)
        if retrans != None:
            userv, data, address = retrans
            if data == None:
                return
            self.transmitData(userv, data, address)
            return
        if data.startswith('SIP/2.0 '):
            try:
                resp = SipResponse(data)
                tid = resp.getTId(True, True)
            except Exception, exception:
                print datetime.now(), 'can\'t parse SIP response from %s:%d: %s:' % (address[0], address[1], str(exception))
                print '-' * 70
                print_exc(file = sys.stdout)
                print '-' * 70
                print data
                print '-' * 70
                sys.stdout.flush()
                self.l1rcache[checksum] = (None, None, None)
                return
            if resp.getSCode()[0] < 100 or resp.getSCode()[0] > 999:
                print datetime.now(), 'invalid status code in SIP response from %s:%d:' % address
                print data
                sys.stdout.flush()
                self.l1rcache[checksum] = (None, None, None)
                return
            resp.rtime = rtime
            if not self.tclient.has_key(tid):
                #print 'no transaction with tid of %s in progress' % str(tid)
                self.l1rcache[checksum] = (None, None, None)
                return
            t = self.tclient[tid]
            if self.nat_traversal and resp.countHFs('contact') > 0 and not check1918(t.address[0]):
                curl = resp.getHFBody('contact').getUrl()
                if check1918(curl.host):
                    curl.host, curl.port = address
            resp.setSource(address)
            self.incomingResponse(resp, t, checksum)
        else:
            if self.req_cb == None:
                return
            try:
                req = SipRequest(data)
                tids = req.getTIds()
            except Exception, exception:
                print datetime.now(), 'can\'t parse SIP request from %s:%d: %s:' % (address[0], address[1], str(exception))
                print '-' * 70
                print_exc(file = sys.stdout)
                print '-' * 70
                print data
                print '-' * 70
                sys.stdout.flush()
                self.l1rcache[checksum] = (None, None, None)
                return
            req.rtime = rtime
            via0 = req.getHFBody('via')
            ahost, aport = via0.getAddr()
            rhost, rport = address
            if self.nat_traversal and rport != aport and check1918(ahost):
                req.nated = True
            if ahost != rhost:
                via0.params['received'] = rhost
            if via0.params.has_key('rport') or req.nated:
                via0.params['rport'] = str(rport)
            if self.nat_traversal and req.countHFs('contact') > 0 and req.countHFs('via') == 1:
                curl = req.getHFBody('contact').getUrl()
                if check1918(curl.host):
                    curl.host, curl.port = address
                    req.nated = True
            req.setSource(address)
            self.incomingRequest(req, checksum, tids, server)

    # 1. Client transaction methods
    def newTransaction(self, msg, resp_cb = None, laddress = None, userv = None):
        t = SipTransaction()
        t.tid = msg.getTId(True, True)
        if self.tclient.has_key(t.tid):
            raise ValueError('BUG: Attempt to initiate transaction with the same TID as existing one!!!')
        t.tout = 0.5
        t.fcode = None
        t.address = msg.getTarget()
        if userv == None:
            if laddress == None:
                t.userv = self.l4r.getServer(t.address)
            else:
                t.userv = self.l4r.getServer(laddress, is_local = True)
        else:
            t.userv = userv
        t.data = msg.localStr(t.userv.laddress[0], t.userv.laddress[1])
        try:
            t.expires = msg.getHFBody('expires').getNum()
            if t.expires <= 0:
                t.expires = 300
        except IndexError:
            t.expires = 300
        if msg.getMethod() == 'INVITE':
            t.needack = True
            t.ack = msg.genACK()
            t.cancel = msg.genCANCEL()
        else:
            t.needack = False
            t.ack = None
            t.cancel = None
        t.cancelPending = False
        t.resp_cb = resp_cb
        t.teA = Timeout(self.timerA, t.tout, 1, t)
        if resp_cb != None:
            t.r408 = msg.genResponse(408, 'Request Timeout')
        t.teB = Timeout(self.timerB, 32.0, 1, t)
        t.teC = None
        t.state = TRYING
        self.tclient[t.tid] = t
        self.transmitData(t.userv, t.data, t.address)
        return t

    def cancelTransaction(self, t, reason = None):
        # If we got at least one provisional reply then (state == RINGING)
        # then start CANCEL transaction, otherwise deffer it
        if t.state != RINGING:
            t.cancelPending = True
        else:
            if reason != None:
                t.cancel.appendHeader(SipHeader(body = reason))
            self.newTransaction(t.cancel, userv = t.userv)

    def incomingResponse(self, msg, t, checksum):
        # In those two states upper level already notified, only do ACK retransmit
        # if needed
        if t.state == TERMINATED:
            return

        if t.state == TRYING:
            # Stop timers
            if t.teA != None:
                t.teA.cancel()
                t.teA = None

        if t.state in (TRYING, RINGING):
            if t.teB != None:
                t.teB.cancel()
                t.teB = None

            if msg.getSCode()[0] < 200:
                # Privisional response - leave everything as is, except that
                # change state and reload timeout timer
                if t.state == TRYING:
                    t.state = RINGING
                    if t.cancelPending:
                        self.newTransaction(t.cancel, userv = t.userv)
                        t.cancelPending = False
                t.teB = Timeout(self.timerB, t.expires, 1, t)
                self.l1rcache[checksum] = (None, None, None)
                if t.resp_cb != None:
                    t.resp_cb(msg)
            else:
                # Final response - notify upper layer and remove transaction
                if t.needack:
                    # Prepare and send ACK if necessary
                    fcode = msg.getSCode()[0]
                    tag = msg.getHFBody('to').getTag()
                    if tag != None:
                        t.ack.getHFBody('to').setTag(tag)
                    rAddr = None
                    if msg.getSCode()[0] >= 200 and msg.getSCode()[0] < 300:
                        # Some hairy code ahead
                        if msg.countHFs('contact') > 0:
                            rTarget = msg.getHFBody('contact').getUrl().getCopy()
                        else:
                            rTarget = None
                        routes = [x.getCopy() for x in msg.getHFBodys('record-route')]
                        routes.reverse()
                        if len(routes) > 0:
                            if not routes[0].getUrl().lr:
                                if rTarget != None:
                                    routes.append(SipRoute(address = SipAddress(url = rTarget)))
                                rTarget = routes.pop(0).getUrl()
                                rAddr = rTarget.getAddr()
                            else:
                                rAddr = routes[0].getAddr()
                        elif rTarget != None:
                            rAddr = rTarget.getAddr()
                        if rTarget != None:
                            t.ack.setRURI(rTarget)
                        if rAddr != None:
                            t.ack.setTarget(rAddr)
                        t.ack.delHFs('route')
                        t.ack.appendHeaders([SipHeader(name = 'route', body = x) for x in routes])
                    if fcode >= 200 and fcode < 300:
                        t.ack.getHFBody('via').genBranch()
                    if rAddr == None:
                        rAddr = t.address
                    self.transmitMsg(t.userv, t.ack, rAddr, checksum)
                else:
                    self.l1rcache[checksum] = (None, None, None)
                if t.resp_cb != None:
                    t.resp_cb(msg)
                del self.tclient[t.tid]
                t.cleanup()

    def timerA(self, t):
        #print 'timerA', t
        self.transmitData(t.userv, t.data, t.address)
        t.tout *= 2
        t.teA = Timeout(self.timerA, t.tout, 1, t)

    def timerB(self, t):
        #print 'timerB', t
        t.teB = None
        if t.teA != None:
            t.teA.cancel()
            t.teA = None
        t.state = TERMINATED
        #print '2: Timeout(self.timerC, 32.0, 1, t)', t
        t.teC = Timeout(self.timerC, 32.0, 1, t)
        if t.resp_cb == None:
            return
        t.r408.rtime = time()
        t.resp_cb(t.r408)
        #try:
        #    t.resp_cb(SipRequest(t.data).genResponse(408, 'Request Timeout'))
        #except:
        #    print 'SipTransactionManager: unhandled exception when processing response!'

    def timerC(self, t):
        #print 'timerC', t
        #print self.tclient
        t.teC = None
        del self.tclient[t.tid]
        t.cleanup()

    # 2. Server transaction methods
    def incomingRequest(self, msg, checksum, tids, server):
        for tid in tids:
            if self.tclient.has_key(tid):
                resp = msg.genResponse(482, 'Loop Detected')
                self.transmitMsg(server, resp, resp.getHFBody('via').getTAddr(), checksum)
                return
        tid = msg.getTId()
        # Fasten seatbelts - bumpy transaction matching code ahead!
        if msg.getMethod() in ('INVITE', 'CANCEL', 'ACK'):
            btid = msg.getTId(wBRN = True)
            t = self.tserver.get(btid, None)
            if t == None:
                t = self.tserver.get(tid, None)
                if t != None and t.branch != btid[3]:
                    if msg.getMethod() == 'INVITE':
                        # Different branch on transaction to which no final reply
                        # has been sent yet - merge requests
                        resp = msg.genResponse(482, 'Loop Detected')
                        self.transmitMsg(server, resp, resp.getHFBody('via').getTAddr(), checksum)
                        return
                    elif msg.getMethod() == 'CANCEL':
                        # CANCEL, but with branch that doesn't match any existing
                        # transactions
                        resp = msg.genResponse(481, 'Call Leg/Transaction Does Not Exist')
                        self.transmitMsg(server, resp, resp.getHFBody('via').getTAddr(), checksum)
                        return
        else:
            t = self.tserver.get(tid, None)
        if t != None:
            #print 'existing transaction'
            if msg.getMethod() == t.method:
                # Duplicate received, check that we have sent any response on this
                # request already
                if t.data != None:
                    self.transmitData(t.userv, t.data, t.address, checksum)
                return
            elif msg.getMethod() == 'CANCEL':
                # RFC3261 says that we have to reply 200 OK in all cases if
                # there is such transaction
                resp = msg.genResponse(200, 'OK')
                self.transmitMsg(t.userv, resp, resp.getHFBody('via').getTAddr(), checksum)
                if t.state in (TRYING, RINGING):
                    self.doCancel(t, msg.rtime, msg)
            elif msg.getMethod() == 'ACK' and t.state == COMPLETED:
                t.state = CONFIRMED
                if t.teA != None:
                    t.teA.cancel()
                    t.teA = None
                t.teD.cancel()
                # We have done with the transaction, no need to wait for timeout
                del self.tserver[t.tid]
                t.cleanup()
                self.l1rcache[checksum] = (None, None, None)
        elif msg.getMethod() == 'ACK':
            # Some ACK that doesn't match any existing transaction.
            # Drop and forget it - upper layer is unlikely to be interested
            # to seeing this anyway.
            print datetime.now(), 'unmatched ACK transaction - ignoring'
            sys.stdout.flush()
            self.l1rcache[checksum] = (None, None, None)
        elif msg.getMethod() == 'CANCEL':
            resp = msg.genResponse(481, 'Call Leg/Transaction Does Not Exist')
            self.transmitMsg(server, resp, resp.getHFBody('via').getTAddr(), checksum)
        else:
            #print 'new transaction', msg.getMethod()
            t = SipTransaction()
            t.tid = tid
            t.state = TRYING
            t.teA = None
            t.teD = None
            t.teE = None
            t.teF = None
            t.method = msg.getMethod()
            t.data = None
            t.address = None
            t.noack_cb = None
            t.cancel_cb = None
            t.checksum = checksum
            if server.laddress[0] not in ('0.0.0.0', '[::]'):
                t.userv = server
            else:
                # For messages received on the wildcard interface find
                # or create more specific server.
                t.userv = self.l4r.getServer(msg.getSource())
            if msg.getMethod() == 'INVITE':
                t.r487 = msg.genResponse(487, 'Request Terminated')
                t.needack = True
                t.branch = msg.getHFBody('via').getBranch()
                try:
                    e = msg.getHFBody('expires').getNum()
                    if e <= 0:
                        e = 300
                except IndexError:
                    e = 300
                t.teE = Timeout(self.timerE, e, 1, t)
            else:
                t.r487 = None
                t.needack = False
                t.branch = None
            self.tserver[t.tid] = t
            for consumer in self.req_consumers.get(t.tid[0], ()):
                consumer = consumer.isYours(msg)
                if consumer != None:
                    rval = consumer.recvRequest(msg)
                    break
            else:
                rval = self.req_cb(msg)
            if rval == None:
                if t.teA != None or t.teD != None or t.teE != None or t.teF != None:
                    return
                if self.tserver.has_key(t.tid):
                    del self.tserver[t.tid]
                t.cleanup()
                return
            resp, cancel_cb, noack_cb = rval
            t.cancel_cb = cancel_cb
            t.noack_cb = noack_cb
            if resp != None:
                self.sendResponse(resp, t)

    def regConsumer(self, consumer, call_id):
        self.req_consumers.setdefault(call_id, []).append(consumer)

    def unregConsumer(self, consumer, call_id):
        # Usually there will be only one consumer per call_id, so that
        # optimize management for this case
        consumers = self.req_consumers.pop(call_id)
        if len(consumers) > 1:
            consumers.remove(consumer)
            self.req_consumers[call_id] = consumers

    def sendResponse(self, resp, t = None, retrans = False):
        #print self.tserver
        if t == None:
            tid = resp.getTId()
            t = self.tserver[tid]
        if t.state not in (TRYING, RINGING) and not retrans:
            raise ValueError('BUG: attempt to send reply on already finished transaction!!!')
        scode = resp.getSCode()[0]
        toHF = resp.getHFBody('to')
        if scode > 100 and toHF.getTag() == None:
            toHF.genTag()
        t.data = resp.localStr(t.userv.laddress[0], t.userv.laddress[1])
        t.address = resp.getHFBody('via').getTAddr()
        self.transmitData(t.userv, t.data, t.address, t.checksum)
        if scode < 200:
            t.state = RINGING
            if self.provisional_retr > 0 and scode > 100:
                if t.teF != None:
                    t.teF.cancel()
                t.teF = Timeout(self.timerF, self.provisional_retr, 1, t)
        else:
            t.state = COMPLETED
            if t.teE != None:
                t.teE.cancel()
                t.teE = None
            if t.teF != None:
                t.teF.cancel()
                t.teF = None
            if t.needack:
                # Schedule removal of the transaction
                t.teD = Timeout(self.timerD, 32.0, 1, t)
                if scode >= 300:
                    # Black magick to allow proxy send us another INVITE with diffetent branch
                    del self.tserver[t.tid]
                    t.tid = list(t.tid)
                    t.tid.append(t.branch)
                    t.tid = tuple(t.tid)
                    self.tserver[t.tid] = t
                # Install retransmit timer if necessary
                t.tout = 0.5
                t.teA = Timeout(self.timerA, t.tout, 1, t)
            else:
                # We have done with the transaction
                del self.tserver[t.tid]
                t.cleanup()

    def doCancel(self, t, rtime = None, req = None):
        if rtime == None:
            rtime = time()
        if t.r487 != None:
            self.sendResponse(t.r487, t, True)
        if t.cancel_cb != None:
            t.cancel_cb(rtime, req)

    def timerD(self, t):
        #print 'timerD'
        t.teD = None
        if t.teA != None:
            t.teA.cancel()
            t.teA = None
        if t.noack_cb != None and t.state != CONFIRMED:
            t.noack_cb()
        del self.tserver[t.tid]
        t.cleanup()

    def timerE(self, t):
        #print 'timerE'
        t.teE = None
        if t.teF != None:
            t.teF.cancel()
            t.teF = None
        if t.state in (TRYING, RINGING):
            if t.r487 != None:
                t.r487.reason = 'Request Expired'
            self.doCancel(t)

    # Timer to retransmit the last provisional reply every
    # 2 seconds
    def timerF(self, t):
        #print 'timerF', t.state
        t.teF = None
        if t.state == RINGING and self.provisional_retr > 0:
            self.transmitData(t.userv, t.data, t.address)
            t.teF = Timeout(self.timerF, self.provisional_retr, 1, t)

    def rCachePurge(self):
        self.l2rcache = self.l1rcache
        self.l1rcache = {}
        self.l4r.rotateCache()

    def transmitMsg(self, userv, msg, address, cachesum, compact = False):
        data = msg.localStr(userv.laddress[0], userv.laddress[1], compact)
        self.transmitData(userv, data, address, cachesum)

    def transmitData(self, userv, data, address, cachesum = None):
        userv.send_to(data, address)
        self.global_config['_sip_logger'].write('SENDING message to %s:%d:\n' % address, data)
        if cachesum != None:
            self.l1rcache[cachesum] = (userv, data, address)
sippy/CCEvents.py
class CCEventRing(CCEventGeneric):
    name = 'CCEventRing'
    pass


Question: from sippy.Timeout import Timeout
from sippy.Signal import Signal
from sippy.SipFrom import SipFrom
from sippy.SipTo import SipTo
from sippy.SipCiscoGUID import SipCiscoGUID
from sippy.UA import UA
from sippy.CCEvents import CCEventRing, CCEventConnect, CCEventDisconnect, CCEventTry, CCEventUpdate, CCEventFail
from sippy.UasStateTrying import UasStateTrying
from sippy.UasStateRinging import UasStateRinging
from sippy.UaStateDead import UaStateDead
from sippy.SipConf import SipConf
from sippy.SipHeader import SipHeader
from sippy.RadiusAuthorisation import RadiusAuthorisation
from sippy.RadiusAccounting import RadiusAccounting
from sippy.FakeAccounting import FakeAccounting
from sippy.SipLogger import SipLogger
from sippy.Rtp_proxy_session import Rtp_proxy_session
from sippy.Rtp_proxy_client import Rtp_proxy_client
from signal import SIGHUP, SIGPROF, SIGUSR1, SIGUSR2
from twisted.internet import reactor
from urllib import unquote
from sippy.Cli_server_local import Cli_server_local
from sippy.SipTransactionManager import SipTransactionManager
from sippy.SipCallId import SipCallId
from re import sub
from time import time
from urllib import quote
from hashlib import md5
from sippy.MyConfigParser import MyConfigParser
import gc, getopt, os, sys
    id = 1
    uaA = None
    uaO = None
    state = None
    cId = None
    cld = None
    eTry = None
    routes = None
    remote_ip = None
    source = None
    acctA = None
    acctO = None
    global_config = None
    rtp_proxy_session = None
    huntstop_scodes = None
    pass_headers = None
    auth_proc = None
    proxied = False
    challenge = None

    def __init__(self, remote_ip, source, global_config, pass_headers):
        self.id = CallController.id
        CallController.id += 1
        self.global_config = global_config
        self.uaA = UA(self.global_config, event_cb = self.recvEvent, conn_cbs = (self.aConn,), disc_cbs = (self.aDisc,), \
          fail_cbs = (self.aDisc,), dead_cbs = (self.aDead,))
        self.uaA.kaInterval = self.global_config['keepalive_ans']
        self.state = CCStateIdle
        self.uaO = None
        self.routes = []
        self.remote_ip = remote_ip
        self.source = source
        self.pass_headers = pass_headers

    def recvEvent(self, event, ua):
        if ua == self.uaA:
            if self.state == CCStateIdle:
                if not isinstance(event, CCEventTry):
                    # Some weird event received
                    self.uaA.recvEvent(CCEventDisconnect(rtime = event.rtime))
                    return
                self.cId, cGUID, self.cli, self.cld, body, auth, self.caller_name = event.getData()
                self.cGUID = cGUID.hexForm()
                if self.cld == None:
                    self.uaA.recvEvent(CCEventFail((500, 'Internal Server Error (1)'), rtime = event.rtime))
                    self.state = CCStateDead
                    return
                if body == None:
                    self.uaA.recvEvent(CCEventFail((500, 'Body-less INVITE is not supported'), rtime = event.rtime))
                    self.state = CCStateDead
                    return
                if self.global_config.has_key('_allowed_pts'):
                    try:
                        body.parse()
                    except:
                        self.uaA.recvEvent(CCEventFail((400, 'Malformed SDP Body'), rtime = event.rtime))
                        self.state = CCStateDead
                        return
                    allowed_pts = self.global_config['_allowed_pts']
                    mbody = body.content.sections[0].m_header
                    if mbody.transport.lower() == 'rtp/avp':
                        mbody.formats = [x for x in mbody.formats if x in allowed_pts]
                        if len(mbody.formats) == 0:
                            self.uaA.recvEvent(CCEventFail((488, 'Not Acceptable Here')))
                            self.state = CCStateDead
                            return
                if self.cld.startswith('nat-'):
                    self.cld = self.cld[4:]
                    body.content += 'a=nated:yes\r\n'
                    event.data = (self.cId, cGUID, self.cli, self.cld, body, auth, self.caller_name)
                if self.global_config.has_key('static_tr_in'):
                    self.cld = re_replace(self.global_config['static_tr_in'], self.cld)
                    event.data = (self.cId, cGUID, self.cli, self.cld, body, auth, self.caller_name)
                if self.global_config.has_key('_rtp_proxy_clients'):
                    self.rtp_proxy_session = Rtp_proxy_session(self.global_config, call_id = self.cId, \
                      notify_socket = global_config['b2bua_socket'], \
                      notify_tag = quote('r %s' % str(self.id)))
                    self.rtp_proxy_session.callee_raddress = (self.remote_ip, 5060)
                self.eTry = event
                self.state = CCStateWaitRoute
                if not self.global_config['auth_enable']:
                    self.username = self.remote_ip
                    self.rDone(((), 0))
                elif auth == None or auth.username == None or len(auth.username) == 0:
                    self.username = self.remote_ip
                    self.auth_proc = self.global_config['_radius_client'].do_auth(self.remote_ip, self.cli, self.cld, self.cGUID, \
                      self.cId, self.remote_ip, self.rDone)
                else:
                    self.username = auth.username
                    self.auth_proc = self.global_config['_radius_client'].do_auth(auth.username, self.cli, self.cld, self.cGUID, 
                      self.cId, self.remote_ip, self.rDone, auth.realm, auth.nonce, auth.uri, auth.response)
                return
            if self.state not in (CCStateARComplete, CCStateConnected, CCStateDisconnecting) or self.uaO == None:
                return
            self.uaO.recvEvent(event)
        else:
            if (isinstance(event, CCEventFail) or isinstance(event, CCEventDisconnect)) and self.state == CCStateARComplete and \
              (isinstance(self.uaA.state, UasStateTrying) or isinstance(self.uaA.state, UasStateRinging)) and len(self.routes) > 0:
                if isinstance(event, CCEventFail):
                    code = event.getData()[0]
                else:
                    code = None
                if code == None or code not in self.huntstop_scodes:
                    self.placeOriginate(self.routes.pop(0))
                    return
            self.uaA.recvEvent(event)

    def rDone(self, results):
        # Check that we got necessary result from Radius
        if len(results) != 2 or results[1] != 0:
            if isinstance(self.uaA.state, UasStateTrying):
                if self.challenge != None:
                    event = CCEventFail((401, 'Unauthorized'))
                    event.extra_header = self.challenge
                else:
                    event = CCEventFail((403, 'Auth Failed'))
                self.uaA.recvEvent(event)
                self.state = CCStateDead
            return
        if self.global_config['acct_enable']:

Answer:             self.acctA = RadiusAccounting(self.global_config, 'answer', \