"""
__author__: Jamin Becker (jamin@packettotal.com)
"""

import os
import sys
import time
import socket
import logging
import pathlib
import warnings
from hashlib import md5


import psutil
import pyshark
import progressbar
from magic import from_buffer
from terminaltables import AsciiTable
from packettotal_sdk.packettotal_api import PacketTotalApi


from honeybot.lib import const


def capture_on_interface(interface, name, timeout=60):
    """
    :param interface: The name of the interface on which to capture traffic
    :param name: The name of the capture file
    :param timeout: A limit in seconds specifying how long to capture traffic
    """

    if timeout < 15:
        logger.error("Timeout must be over 15 seconds.")
        return
    if not sys.warnoptions:
        warnings.simplefilter("ignore")
    start = time.time()
    widgets = [
        progressbar.Bar(marker=progressbar.RotatingMarker()),
        ' ',
        progressbar.FormatLabel('Packets Captured: %(value)d'),
        ' ',
        progressbar.Timer(),
    ]
    progress = progressbar.ProgressBar(widgets=widgets)
    capture = pyshark.LiveCapture(interface=interface, output_file=os.path.join('tmp', name))
    pcap_size = 0
    for i, packet in enumerate(capture.sniff_continuously()):
        progress.update(i)
        if os.path.getsize(os.path.join('tmp', name)) != pcap_size:
            pcap_size = os.path.getsize(os.path.join('tmp', name))
        if not isinstance(packet, pyshark.packet.packet.Packet):
            continue
        if time.time() - start > timeout:
            break
        if pcap_size > const.PT_MAX_BYTES:
            break
    capture.clear()
    capture.close()
    return pcap_size


def check_auth():
    home = str(pathlib.Path.home())
    key = ''
    auth_path = os.path.join(home, 'honeybot.auth')
    if not os.path.exists(auth_path):
        print('HoneyBot requires a PacketTotal API key.')
        print('Signup at: \n\t: https://packettotal.com/api.html\n')
    else:
        key = open(auth_path, 'r').read()
    while PacketTotalApi(key).usage().status_code == 403:
        print('Invalid API Key. Try again.')
        key = input('API Key: ')
        open(auth_path, 'w').write(key)

    return open(auth_path, 'r').read()


def get_filepath_md5_hash(file_path):
    """
    :param file_path: path to the file being hashed
    :return: the md5 hash of a file
    """
    with open(file_path, 'rb') as afile:
       return get_file_md5_hash(afile)


def get_str_md5_hash(s):
    return md5(str(s).encode('utf-8')).hexdigest()


def get_mac_address_of_interface(interface):
    """
    :param interface: The friendly name of a network interface
    :return: the MAC address associated with that interface
    """
    for k,v in psutil.net_if_addrs().items():
        if interface == k:
            for item in v:
                try:
                    if item.family == socket.AF_LINK:
                        return item.address
                except AttributeError:
                    # Linux
                    if item.family == socket.AF_PACKET:
                        return item.address
    return None


def gen_unique_id(interface):
    """
    Generates a unique ID based on your MAC address that will be used to tag all PCAPs uploaded to PacketTotal.com
    This ID can be used to search and view PCAPs you have uploaded.

    :param interface: The friendly name of a network interface
    :return: A unique id
    """
    mac_address = get_mac_address_of_interface(interface)
    if mac_address:
        return get_str_md5_hash(get_str_md5_hash(mac_address))[0:15]
    return None


def get_file_md5_hash(fh):
    """
    :param fh: file handle
    :return: the md5 hash of the file
    """
    block_size = 65536
    md5_hasher = md5()
    buf = fh.read(block_size)
    while len(buf) > 0:
        md5_hasher.update(buf)
        buf = fh.read(block_size)
    return md5_hasher.hexdigest()


def get_network_interfaces():
    """
    :return: A list of valid interfaces and their addresses
    """
    return psutil.net_if_addrs().items()


def is_packet_capture(bytes):
    """
    :param bytes: raw bytes
    :return: True is valid pcap or pcapng file
    """
    result = from_buffer(bytes)
    valid = "pcap-ng" in result or "tcpdump" in result or "NetMon" in result or 'pcap capture file' in result
    return valid


def mkdir_p(path):
    """
    :param path: Path to the new directory to create
    """

    pathlib.Path(path).mkdir(parents=True, exist_ok=True)


def listen_on_interface(interface, timeout=60):
    """
    :param interface: The name of the interface on which to capture traffic
    :return: generator containing live packets
    """

    start = time.time()
    capture = pyshark.LiveCapture(interface=interface)

    for item in capture.sniff_continuously():
        if timeout and time.time() - start > timeout:
            break
        yield item


def print_network_interaces():
    """
    :return: Prints a human readable representation of the available network interfaces
    """

    for intf, items in get_network_interfaces():
        table = [["family", "address", "netmask", "broadcast", "ptp"]]
        for item in items:
            family, address, netmask, broadcast, ptp = item
            table.append([str(family), str(address), str(netmask), str(broadcast), str(ptp)])
        print(AsciiTable(table_data=table, title=intf).table)
        print('\n')


def print_analysis_disclaimer():
    print("""
        WARNING: Analysis will result in the network traffic becoming public at https://packettotal.com.
        
        ADVERTENCIA: El análisis hará que el tráfico de la red se haga público en https://packettotal.com.
        
        WARNUNG: Die Analyse führt dazu, dass der Netzwerkverkehr unter https://packettotal.com öffentlich wird.
        
        ПРЕДУПРЕЖДЕНИЕ. Анализ приведет к тому, что сетевой трафик станет общедоступным на https://packettotal.com.
        
        चेतावनी: विश्लेषण का परिणाम नेटवर्क ट्रैफिक https://packettotal.com पर सार्वजनिक हो जाएगा
        
        警告:分析将导致网络流量在https://packettotal.com上公开
        
        警告:分析により、ネットワークトラフィックはhttps://packettotal.comで公開されます。
        
        tahdhir: sayuadiy altahlil 'iilaa 'an tusbih harakat murur alshabakat eamat ealaa https://packettotal.com

    """)

    answer = input('Continue? [Y/n]: ')
    if answer.lower() == 'n':
        exit(0)


def print_pt_ascii_logo():
    """
    :return: Prints a PacketTotal.com (visit https://PacketTotal.com!)
    """

    logo = """
                                                           ,,*****,                 ,****,
                                                   ****/**,  /              ,*//*,
                                                   ****/**,  ,,**********,. ******
                            ..  .,*****,  ..       ********  ************,.   .
                    .  .,,***,  ,******, .,***,,..           *****////***,  ,***,
                 ...  ,******,  ,******, .,********          *****////***,. ****,
            .,,****,  ,******,  ,******,  ,********          ************,  .///
             /////* // ////// /( ////// /( */////             ************
    ****,. ,,******.  ,******.  ,******,  .******,,.,******,.               *
    *****, ********,  ,******,  ,******, .,********.,********      ,*******,
    *****, ********,  ,******,  ,******, .,********.,********      ****//**,
    *****, /*******,  ,*******  *//////*  ********/ ,********      ****//**,
    ////* / .////// ((.(((((( ## (((((( (& /(((((  % //////        ,******** .
    ,,,,,. ,,,,,,,,.  ,,*****,  ,******,  ,*****,,,..,,,,,,,. .,,,,,,
    *****, /*******,  *//////*  *//////* .*//////*/.*********,,*****,
    *****, /*******,  *//////*  (.     (  *////////.*********,,******
    *****, /********  */////(* ..,,,,,,..  (/////// ********* *******
    ////* / ,/((((/ #@,####*..,**********,. /####. & /(((((. @ *////
    ,,,,,. ..,,,,*,,  ,**,  ,*****////*****,./***,. ,,,,,,,.. .,,,,,
     ,***, /*****//*  */// .,***//(##((/****, /////,*//******,,***,
     ****, /*****//*  *//* ,****/(%&&%(//**,, ////(,*//******,,****
     ****, /*****//*  *//*  ,***//(##(//***** *///( *//******.***,
      *** ( **///// #@//((. ******////****** /(((* @ //////*   **,
       ,,. ..,,,,,,,  ,****,. ************ .,****,. ,,,,,,,.. ,,*
        *, /********  *//////,   ,****,   ,////////.*********,,*
         * /*******,  *//////*  ,******, .*////////.*********,,
           /********  *//////*  *//////*  *//////*/ *********
            *******,# ////////# ////////#  //////*   /******
             ,,,,,..((.,,,,,,.(#.,,,,,,.(#.,,,,,,..(..,,,,,
            * *****,  ,******,  *//////* .,*******/.,*****
                ***,  ,******,  ,******, .,********.,*** /
               * ,*,  ,******,  ,******,  ,********.,*  .
                      *******/  *******/  ,*******    ,
                       ,,,,,..// .,,,,..// .,,,,,
                      / .****,  ,******, .,****, /
                         / ***  ,******,  ***
                                ********   #
    
                                    - honeybot: Capture and analyze network traffic; powered by PacketTotal.com
                                               : VERSION: {}
    """.format(const.VERSION)
    print(logo)



# Setup Logging

mkdir_p('logs/')
mkdir_p('tmp/')
logger = logging.getLogger('honeybot.utils')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('logs/honeybot.log')
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(ch)