
from __future__ import absolute_import


from ipaddress import ip_address, ip_network
from sys import version_info
from time import time

from core.tools import (
    get_local_ip,
    get_utc_time,
    write_event
)

from twisted.protocols.sip import Base, MessagesParser
from twisted.python.log import msg


if version_info[0] >= 3:
    def unicode(x):
        return x


class SIPServer(Base):
    def __init__(self, options):
        self.cfg = options
        self.messages = []
        self.parser = MessagesParser(self.addMessage)

    def handle_request(self, message, addr):
        headers = {}
        for item, value in message.headers.items():
            headers.update({item: ','.join(value)})

        self.report_event(addr, str(message), headers)

        request = message.method
        if request not in [
            'INVITE', 'ACK', 'BYE', 'CANCEL', 'REGISTER',
            'OPTIONS', 'PRACK', 'SUBSCRIBE', 'NOTIFY',
            'PUBLISH', 'INFO', 'REFER', 'MESSAGE', 'UPDATE'
        ]:
            response = self.responseFromRequest(501, message)
        elif request in ['INVITE', 'REGISTER']:
            response = self.responseFromRequest(401, message)
            response.addHeader(
                'WWW-Authenticate',
                'Digest algorithm=MD5, realm="asterisk", nonce="36aa22cf"'
            )
        elif request == 'SUBSCRIBE':
            response = self.responseFromRequest(403, message)
        elif request in ['NOTIFY', 'PUBLISH']:
            response = self.responseFromRequest(405, message)
            # Twisted doesn't support SIP error 489, do it manually
            response.code = 489
            response.phrase = 'Bad Event'
        elif request == 'REFER':
            response = self.responseFromRequest(603, message)
        elif request == 'MESSAGE':
            response = self.responseFromRequest(415, message)
        elif request == 'OPTIONS':
            response = self.responseFromRequest(200, message)
        elif request == 'ACK':
            return
        else:
            response = self.responseFromRequest(481, message)
        response.addHeader('Server', 'Asterisk PBX 16.28.0')
        response.addHeader('Allow', 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE')
        response.addHeader('Supported', 'replaces, timer')
        response.addHeader('Accept', 'application/sdp')
        response.addHeader('Content-Length', 0)
        response.creationFinished()
        self.deliverResponse(response)

    def report_event(self, addr, message, headers):
        ip = addr[0]
        for network in self.cfg['blacklist']:
            if ip_address(unicode(ip)) in ip_network(unicode(network)):
                return
        unix_time = time()
        port = addr[1]
        event = {
            'eventid': 'siphoney.request',
            'timestamp': get_utc_time(unix_time),
            'unixtime': unix_time,
            'src_ip': ip,
            'src_port': port,
            'dst_port': self.cfg['port'],
            'sensor': self.cfg['sensor'],
            'dst_ip': self.cfg['public_ip'] if self.cfg['report_public_ip'] else get_local_ip(),
            'message': message,
            'headers': headers
        }
        msg('{} from {}:{}.'.format(message, ip, self.cfg['port']))
        write_event(event, self.cfg)
