#!/usr/bin/python

import random
import time

from twisted.internet.protocol import DatagramProtocol
from twisted.protocols.sip import Base, Request
from twisted.internet import reactor

RTP_PORT = 26300
SIP_PORT = 5061

def random_string(length):
    return ''.join([ random.choice('0123456789abcdef') for x in range(0,length) ])

class Media(DatagramProtocol):
    """
    RTP handler which just echoes received packets.
    """
    def datagramReceived(self, data, (host, port)):
        self.transport.write(data, (host, port))

class Signaling(Base):
    """
    SIP handler which accepts invites, but fails reinvites.
    """
    dialogs = set()

    def handle_request(self, message, addr):
        if message.method == 'INVITE':
            # send 100/trying then sleep a bit
            self.deliverResponse(self.responseFromRequest(100, message))
            time.sleep(3)

            call_id = message.headers['call-id'][0]
            if call_id not in self.dialogs:
                self.dialogs.add(call_id)

                # accept initial invite
                response = self.responseFromRequest(200, message)
                response.body = """v=0\r
o=- 1392392721 1392392723 IN IP4 127.0.0.1\r
s=-\r
c=IN IP4 127.0.0.1\r
t=0 0\r
m=audio %s RTP/AVP 0 101\r
a=rtpmap:0 PCMU/8000\r
a=rtpmap:101 telephone-event/8000\r
a=fmtp:101 0-15\r
a=silenceSupp:off - - - -\r
""" % RTP_PORT
                response.headers['to'][0] += ';tag=%s' % random_string(8)
                response.addHeader('Contact', '<sip:127.0.0.1:%s>' % SIP_PORT)
                response.addHeader('Content-Type', 'application/sdp')
                response.addHeader('Content-Length', len(response.body))
                self.deliverResponse(response)
            else:
                # fail reinvite with a 500 error
                self.deliverResponse(self.responseFromRequest(500, message))

                # terminate call
                contact = message.headers['contact'][0][1:-1]
                request = Request('BYE', contact)
                request.addHeader('From', message.headers['to'][0])
                request.addHeader('To', message.headers['from'][0])
                request.addHeader('Call-ID', call_id)
                request.addHeader('CSeq', '1 BYE')
                request.addHeader('Via', "SIP/2.0/UDP 127.0.0.1:%s;branch=z9hG4bK%s" % (SIP_PORT, random_string(8)))
                self.sendMessage(request.uri, request) 

        elif message.method in ['BYE', 'CANCEL']:
            self.deliverResponse(self.responseFromRequest(200, message))

    def handle_response(self, message, addr):
        pass

reactor.listenUDP(RTP_PORT, Media())
reactor.listenUDP(SIP_PORT, Signaling())
reactor.run()
