
from datetime import datetime
from ipaddress import ip_address, ip_network
from os import makedirs, path
from socket import socket, AF_INET, SOCK_DGRAM
from sys import version_info

from core.config import CONFIG

from pytz import timezone

from twisted.python.log import msg

try:
    from urllib.request import urlopen
except ImportError:
    from urllib import urlopen


if version_info[0] >= 3:
    def decode(x):
        return x.decode('utf-8', errors='ignore')
    def encode(x):
        return x.encode()
    def ord(x):
        return x
    def to_bytes(x):
        return bytes(x, 'ascii')
    def to_int(x):
        return x
    def unicode(x):
        return x
else:
    def decode(x):
        return x
    def encode(x):
        return x
    def to_bytes(x):
        return bytes(x)
    def to_int(x):
        return ord(x)


def mkdir(dir_path):
    if not dir_path:
        return
    if path.exists(dir_path) and path.isdir(dir_path):
        return
    makedirs(dir_path)


def import_plugins(cfg):
    # Load output modules (inspired by the Cowrie honeypot)
    msg('Loading the plugins...')
    output_plugins = []
    general_options = cfg
    for x in CONFIG.sections():
        if not x.startswith('output_'):
            continue
        if CONFIG.getboolean(x, 'enabled') is False:
            continue
        engine = x.split('_')[1]
        try:
            output = __import__('output_plugins.{}'.format(engine),
                                globals(), locals(), ['output'], 0).Output(general_options)
            output_plugins.append(output)
            msg('Loaded output engine: {}'.format(engine))
        except ImportError as e:
            msg('Failed to load output engine: {} due to ImportError: {}'.format(engine, e))
        except Exception as e:
            msg('Failed to load output engine: {} {}'.format(engine, e))
    return output_plugins


def stop_plugins(cfg):
    msg('Stoping the plugins...')
    for plugin in cfg['output_plugins']:
        try:
            plugin.stop()
        except Exception as e:
            msg(e)
            continue


def get_public_ip(ip_reporter):
    try:
        if version_info[0] < 3:
            return urlopen(ip_reporter).read().decode('latin1', errors='replace').encode('utf-8')
        else:
            return decode(urlopen(ip_reporter).read())
    except:
        return None


def get_local_ip():
    s = socket(AF_INET, SOCK_DGRAM)
    try:
        s.connect(('10.255.255.255', 1))
        ip = s.getsockname()[0]
    except:
        ip = '127.0.0.1'
    finally:
        s.close()
    return ip


def get_utc_time(unix_time):
    return datetime.fromtimestamp(unix_time, tz=timezone('UTC')).isoformat() + 'Z'


def write_event(event, cfg):
    ip = event['src_ip']
    for network in cfg['blacklist']:
        if ip_address(unicode(ip)) in ip_network(unicode(network)):
            return
    output_plugins = cfg['output_plugins']
    for plugin in output_plugins:
        try:
            plugin.write(event)
        except Exception as e:
            msg(e)
            continue


def geolocate(remote_ip, reader_city, reader_asn):
    try:
        response_city = reader_city.city(remote_ip)
        city = response_city.city.name
        if city is None:
            city = ''
        else:
            city = decode(city.encode('utf-8'))
        country = response_city.country.name
        if country is None:
            country = ''
            country_code = ''
        else:
            country = decode(country.encode('utf-8'))
            country_code = decode(response_city.country.iso_code.encode('utf-8'))
    except Exception as e:
        msg(e)
        city = ''
        country = ''
        country_code = ''

    try:
        response_asn = reader_asn.asn(remote_ip)
        if response_asn.autonomous_system_organization is None:
            org = ''
        else:
            org = decode(response_asn.autonomous_system_organization.encode('utf-8'))

        if response_asn.autonomous_system_number is not None:
            asn_num = response_asn.autonomous_system_number
        else:
            asn_num = 0
    except Exception as e:
        msg(e)
        org = ''
        asn_num = 0
    return country, country_code, city, org, asn_num
