"""

Common terminal messages used across Empire.

Titles, agent displays, listener displays, etc.

"""

import os
import time
import textwrap

# Empire imports
import helpers


###############################################################
#
# Messages
#
###############################################################

def title(version):
    """
    Print the tool title, with version.
    """
    os.system('clear')
    print "================================================================"
    # print ' [Empire]  PowerShell/Python post-exploitation framework'
    print " [Empire]  Post-Exploitation Framework"
    print '================================================================'
    print " [Version] %s | [Web] https://github.com/empireProject/Empire" % (version)
    print '================================================================'
    print """
   _______ .___  ___. .______    __  .______       _______
  |   ____||   \/   | |   _  \  |  | |   _  \     |   ____|
  |  |__   |  \  /  | |  |_)  | |  | |  |_)  |    |  |__
  |   __|  |  |\/|  | |   ___/  |  | |      /     |   __|
  |  |____ |  |  |  | |  |      |  | |  |\  \----.|  |____
  |_______||__|  |__| | _|      |__| | _| `._____||_______|

"""

def loading():
    """
    Print and ascii loading screen.
    """

    print """
                              `````````
                         ``````.--::///+
                     ````-+sydmmmNNNNNNN
                   ``./ymmNNNNNNNNNNNNNN
                 ``-ymmNNNNNNNNNNNNNNNNN
               ```ommmmNNNNNNNNNNNNNNNNN
              ``.ydmNNNNNNNNNNNNNNNNNNNN
             ```odmmNNNNNNNNNNNNNNNNNNNN
            ```/hmmmNNNNNNNNNNNNNNNNMNNN
           ````+hmmmNNNNNNNNNNNNNNNNNMMN
          ````..ymmmNNNNNNNNNNNNNNNNNNNN
          ````:.+so+//:---.......----::-
         `````.`````````....----:///++++
        ``````.-/osy+////:::---...-dNNNN
        ````:sdyyydy`         ```:mNNNNM
       ````-hmmdhdmm:`      ``.+hNNNNNNM
       ```.odNNmdmmNNo````.:+yNNNNNNNNNN
       ```-sNNNmdh/dNNhhdNNNNNNNNNNNNNNN
       ```-hNNNmNo::mNNNNNNNNNNNNNNNNNNN
       ```-hNNmdNo--/dNNNNNNNNNNNNNNNNNN
      ````:dNmmdmd-:+NNNNNNNNNNNNNNNNNNm
      ```/hNNmmddmd+mNNNNNNNNNNNNNNds++o
     ``/dNNNNNmmmmmmmNNNNNNNNNNNmdoosydd
     `sNNNNdyydNNNNmmmmmmNNNNNmyoymNNNNN
     :NNmmmdso++dNNNNmmNNNNNdhymNNNNNNNN
     -NmdmmNNdsyohNNNNmmNNNNNNNNNNNNNNNN
     `sdhmmNNNNdyhdNNNNNNNNNNNNNNNNNNNNN
       /yhmNNmmNNNNNNNNNNNNNNNNNNNNNNmhh
        `+yhmmNNNNNNNNNNNNNNNNNNNNNNmh+:
          `./dmmmmNNNNNNNNNNNNNNNNmmd.
            `ommmmmNNNNNNNmNmNNNNmmd:
             :dmmmmNNNNNmh../oyhhhy:
             `sdmmmmNNNmmh/++-.+oh.
              `/dmmmmmmmmdo-:/ossd:
                `/ohhdmmmmmmdddddmh/
                   `-/osyhdddddhyo:
                        ``.----.`

                Welcome to the Empire"""
    time.sleep(3)
    os.system('clear')


def wrap_string(data, width=40, indent=32, indentAll=False, followingHeader=None):
    """
    Print a option description message in a nicely
    wrapped and formatted paragraph.

    followingHeader -> text that also goes on the first line
    """

    data = str(data)

    if len(data) > width:
        lines = textwrap.wrap(textwrap.dedent(data).strip(), width=width)

        if indentAll:
            returnString = ' ' * indent + lines[0]
            if followingHeader:
                returnString += " " + followingHeader
        else:
            returnString = lines[0]
            if followingHeader:
                returnString += " " + followingHeader
        i = 1
        while i < len(lines):
            returnString += "\n" + ' ' * indent + (lines[i]).strip()
            i += 1
        return returnString
    else:
        return data.strip()


def wrap_columns(col1, col2, width1=24, width2=40, indent=31):
    """
    Takes two strings of text and turns them into nicely formatted column output.

    Used by display_module()
    """

    lines1 = textwrap.wrap(textwrap.dedent(col1).strip(), width=width1)
    lines2 = textwrap.wrap(textwrap.dedent(col2).strip(), width=width2)

    result = ''

    limit = max(len(lines1), len(lines2))

    for x in xrange(limit):

        if x < len(lines1):
            if x != 0:
                result += ' ' * indent
            result += '{line: <0{width}s}'.format(width=width1, line=lines1[x])
        else:
            if x == 0:
                result += ' ' * width1
            else:
                result += ' ' * (indent + width1)

        if x < len(lines2):
            result += '  ' + '{line: <0{width}s}'.format(width=width2, line=lines2[x])

        if x != limit-1:
            result += "\n"

    return result


def display_options(options, color=True):
    """
    Take a dictionary and display it nicely.
    """
    for key in options:
        if color:
            print "\t%s\t%s" % (helpers.color('{0: <16}'.format(key), "green"), wrap_string(options[key]))
        else:
            print "\t%s\t%s" % ('{0: <16}'.format(key), wrap_string(options[key]))


def display_agents(agents):
    """
    Take a dictionary of agents and build the display for the main menu.
    """

    if len(agents) > 0:

        print ''
        print helpers.color("[*] Active agents:\n")
        print "  Name            Lang  Internal IP     Machine Name    Username            Process             Delay    Last Seen"
        print "  ---------       ----  -----------     ------------    ---------           -------             -----    --------------------"

        for agent in agents:

            if str(agent['high_integrity']) == '1':
                # add a * to the username if it's high integrity
                agent['username'] = '*' + str(agent['username'])
            if not agent['language'] or agent['language'] == '':
                agent['language'] = 'X'
            elif agent['language'].lower() == 'powershell':
                agent['language'] = 'ps'
            elif agent['language'].lower() == 'python':
                agent['language'] = 'py'
            else:
                agent['language'] = 'X'

            print "  %.16s%.6s%.16s%.16s%.20s%.20s%.9s%.20s" % ('{0: <16}'.format(agent['name']), '{0: <6}'.format(agent['language']), '{0: <16}'.format(agent['internal_ip']), '{0: <16}'.format(agent['hostname']), '{0: <20}'.format(agent['username']), '{0: <20}'.format(str(agent['process_name']) + "/" + str(agent['process_id'])), '{0: <9}'.format(str(agent['delay']) + "/"  +str(agent['jitter'])), agent['lastseen_time'])

        print ''
    else:
        print helpers.color('[!] No agents currently registered')


def display_agent(agent, returnAsString=False):
    """
    Display an agent all nice-like.

    Takes in the tuple of the raw agent database results.
    """

    if returnAsString:
        agentString = "\n[*] Agent info:\n"
        for key, value in agent.iteritems():
            if key != 'functions' and key != 'takings' and key != 'results':
                agentString += "  %s\t%s\n" % ('{0: <16}'.format(key), wrap_string(value, width=70))
        return agentString + '\n'
    else:
        print helpers.color("\n[*] Agent info:\n")
        for key, value in agent.iteritems():
            if key != 'functions' and key != 'takings' and key != 'results':
                print "\t%s\t%s" % (helpers.color('{0: <16}'.format(key), "blue"), wrap_string(value, width=70))
        print ''


def display_listeners(listeners, type = "Active"):
    """
    Take an active listeners list and display everything nicely.
    """

    if len(listeners) > 0:
        print ''
        print helpers.color("[*] %s listeners:\n" % type)

        print "  Name              Module          Host                                 Delay/Jitter   KillDate"
        print "  ----              ------          ----                                 ------------   --------"

        for listenerName, listener in listeners.iteritems():

            moduleName = listener['moduleName']
            if 'Host' in listener['options']:
                host = listener['options']['Host']['Value']
            else:
                host = ''

            if 'DefaultDelay' in listener['options']:
                defaultDelay = listener['options']['DefaultDelay']['Value']
            else:
                defaultDelay = 'n/a'

            if 'DefaultJitter' in listener['options']:
                defaultJitter = listener['options']['DefaultJitter']['Value']
            else:
                defaultJitter = ''
            
            if defaultDelay == 'n/a':
                connectInterval = 'n/a'
            else:
                connectInterval = "%s/%s" % (defaultDelay, defaultJitter)

            if 'KillDate' in listener['options']:
                killDate = listener['options']['KillDate']['Value']
            else:
                killDate = 'n/a'

            print "  %s%s%s%s%s" % ('{0: <18}'.format(listenerName), '{0: <16}'.format(moduleName), '{0: <37}'.format(host), '{0: <15}'.format(connectInterval), '{0: <12}'.format(killDate))

        print ''

    else:
        if(type.lower() != "inactive"):
            print helpers.color("[!] No listeners currently %s " % type.lower())


def display_active_listener(listener):
    """
    Displays an active listener's information structure.
    """

    print "\n%s Options:\n" % (listener['options']['Name']['Value'])
    print "  Name              Required    Value                            Description"
    print "  ----              --------    -------                          -----------"

    for option, values in listener['options'].iteritems():
        # if there's a long value length, wrap it
        if len(str(values['Value'])) > 33:
            print "  %s%s%s" % ('{0: <18}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(wrap_string(values['Value'], width=32, indent=32, followingHeader=values['Description'])))
        else:
            print "  %s%s%s%s" % ('{0: <18}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(values['Value']), values['Description'])

    print "\n"


def display_listener_module(listener):
    """
    Displays a listener module's information structure.
    """

    print '\n{0: >10}'.format("Name: ") + str(listener.info['Name'])
    print '{0: >10}'.format("Category: ") + str(listener.info['Category'])

    print "\nAuthors:"
    for author in listener.info['Author']:
        print "  " +author

    print "\nDescription:"
    desc = wrap_string(listener.info['Description'], width=60, indent=2, indentAll=True)
    if len(desc.splitlines()) == 1:
        print "  " + str(desc)
    else:
        print desc

    if 'Comments' in listener.info:
        comments = listener.info['Comments']
        if isinstance(comments, list):
            comments = ' '.join(comments)
        if comments.strip() != '':
            print "\nComments:"
            if isinstance(comments, list):
                comments = ' '.join(comments)
            comment = wrap_string(comments, width=60, indent=2, indentAll=True)
            if len(comment.splitlines()) == 1:
                print "  " + str(comment)
            else:
                print comment


    print "\n%s Options:\n" % (listener.info['Name'])
    print "  Name              Required    Value                            Description"
    print "  ----              --------    -------                          -----------"

    for option, values in listener.options.iteritems():
        # if there's a long value length, wrap it
        if len(str(values['Value'])) > 33:
            print "  %s%s%s" % ('{0: <18}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(wrap_string(values['Value'], width=32, indent=32, followingHeader=values['Description'])))
        else:
            print "  %s%s%s%s" % ('{0: <18}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(values['Value']), values['Description'])

    print "\n"


def display_stager(stager):
    """
    Displays a stager's information structure.
    """

    print "\nName: " + stager.info['Name']

    print "\nDescription:"
    desc = wrap_string(stager.info['Description'], width=50, indent=2, indentAll=True)
    if len(desc.splitlines()) == 1:
        print "  " + str(desc)
    else:
        print desc

    # print out any options, if present
    if stager.options:
        print "\nOptions:\n"
        print "  Name             Required    Value             Description"
        print "  ----             --------    -------           -----------"

        for option, values in stager.options.iteritems():
            print "  %s%s%s%s" % ('{0: <17}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <18}'.format(values['Value']), wrap_string(values['Description'], indent=49))

    print "\n"


def display_module(moduleName, module):
    """
    Displays a module's information structure.
    """

    print '\n{0: >20}'.format("Name: ") + str(module.info['Name'])
    print '{0: >20}'.format("Module: ") + str(moduleName)
    if 'NeedsAdmin' in module.info:
        print '{0: >20}'.format("NeedsAdmin: ") + ("True" if module.info['NeedsAdmin'] else "False")
    if 'OpsecSafe' in module.info:
        print '{0: >20}'.format("OpsecSafe: ") + ("True" if module.info['OpsecSafe'] else "False")
    if 'Language' in module.info:
        print '{0: >20}'.format("Language: ") + str(module.info['Language'])
    if 'MinLanguageVersion' in module.info:
        print '{0: >20}'.format("MinLanguageVersion: ") + str(module.info['MinLanguageVersion'])
    if 'Background' in module.info:
        print '{0: >20}'.format("Background: ") + ("True" if module.info['Background'] else "False")
    if 'OutputExtension' in module.info:
        print '{0: >20}'.format("OutputExtension: ") + (str(module.info['OutputExtension']) if module.info['OutputExtension'] else "None")

    print "\nAuthors:"
    for author in module.info['Author']:
        print "  " +author

    print "\nDescription:"
    desc = wrap_string(module.info['Description'], width=60, indent=2, indentAll=True)
    if len(desc.splitlines()) == 1:
        print "  " + str(desc)
    else:
        print desc

    if 'Comments' in module.info:
        comments = module.info['Comments']
        if isinstance(comments, list):
            comments = ' '.join(comments)
        if comments.strip() != '':
            print "\nComments:"
            if isinstance(comments, list):
                comments = ' '.join(comments)
            comment = wrap_string(comments, width=60, indent=2, indentAll=True)
            if len(comment.splitlines()) == 1:
                print "  " + str(comment)
            else:
                print comment

    # print out any options, if present
    if module.options:

        # get the size for the first column
        maxNameLen = len(max(module.options.keys(), key=len))

        print "\nOptions:\n"
        print "  %sRequired    Value                     Description" %('{:<{}s}'.format("Name", maxNameLen+1))
        print "  %s--------    -------                   -----------" %('{:<{}s}'.format("----", maxNameLen+1))

        for option, values in module.options.iteritems():
            print "  %s%s%s" % ('{:<{}s}'.format(str(option), maxNameLen+1), '{0: <12}'.format(("True" if values['Required'] else "False")), wrap_columns(str(values['Value']), str(values['Description']), indent=(31 + (maxNameLen-16))))

    print ''


def display_module_search(moduleName, module):
    """
    Displays the name/description of a module for search results.
    """

    # Suffix modules requring elevated context with '*'
    if module.info['NeedsAdmin']:
        print " %s*\n" % (helpers.color(moduleName, 'blue'))
    else:
        print " %s\n" % (helpers.color(moduleName, 'blue'))
    # width=40, indent=32, indentAll=False,

    lines = textwrap.wrap(textwrap.dedent(module.info['Description']).strip(), width=70)
    for line in lines:
        print "\t" + line

    print "\n"


def display_credentials(creds):
    """
    Take a credential array and display everything nicely.
    """

    print helpers.color("\nCredentials:\n", "blue")
    print "  CredID  CredType   Domain                   UserName         Host             Password"
    print "  ------  --------   ------                   --------         ----             --------"

    for cred in creds:
        # (id, credtype, domain, username, password, host, notes, sid)
        credID = cred[0]
        credType = cred[1]
        domain = cred[2]
        username = cred[3]
        password = cred[4]
        host = cred[5]

        print "  %s%s%s%s%s%s" % ('{0: <8}'.format(credID), '{0: <11}'.format(credType), '{0: <25}'.format(domain), '{0: <17}'.format(username), '{0: <17}'.format(host), password)

    print ''
