import gzip
import re


#
# ParseLogs.py
# Parsing component of Logalyzer.  Original: https://github.com/hatRiot/logalyzer
# Converted to python3.6 by @programmerchad
#

# log object
# Stuck into a dictionary by user:Log, where log houses
# logs, fails, successes, logged IPs, and commands used
class Log:
    # dump date of first log
    def first_date(self):
        if len(self.logs) > 0:
            date = None
            i = 0
            # sometimes the first few aren't right, so look
            # until we find one
            while i < len(self.logs) and date is None:
                date = ParseDate(self.logs[i])
                i += 1
            return date

    # dump date of last log
    def last_date(self):
        if len(self.logs) > 0:
            return ParseDate(self.logs[len(self.logs) - 1])

    def __init__(self, usr):
        self.usr = usr
        self.logs = []
        self.fail_logs = []
        self.succ_logs = []
        self.ips = []
        self.commands = []


# parse user from various lines
def ParseUsr(line):
    usr = None
    if "Accepted password" in line:
        usr = re.search(r'(\bfor\s)(\w+)', line)
    elif "sudo:" in line:
        usr = re.search(r'(sudo:\s+)(\w+)', line)
    elif "authentication failure" in line:
        usr = re.search(r'USER=\w+', line)
    elif "for invalid user" in line:
        usr = re.search(r'(\buser\s)(\w+)', line)
    if usr is not None:
        return usr.group(2)


# parse an IP from a line
def ParseIP(line):
    ip = re.search(r'(\bfrom\s)(\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b)', line)
    if ip is not None:
        return ip.group(2)


# parse a date from the line
def ParseDate(line):
    date = re.search(r'^[A-Za-z]{3}\s*[0-9]{1,2}\s[0-9]{1,2}:[0-9]{2}:[0-9]{2}', line)
    if date is not None:
        return date.group(0)


# parse a command from a line
def ParseCmd(line):
    # parse command to end of line
    cmd = re.search(r'(\bCOMMAND=)(.+?$)', line)
    if cmd is not None:
        return cmd.group(2)


# begin parsing the passed LOG
def ParseLogs(log):
    # initialize the dictionary
    logs = {}

    # parse the log
    f = None
    try:
        f = gzip.open(log, 'r') if '.gz' in log else open(log, 'r')
        log = f.read()
    except Exception as e:
        print('[-] Error opening \'%s\': %s' % (log, e))
        return None
    finally:
        if f is not None:
            f.close()

    for line in log.split('\n'):
        # match a login
        if "Accepted password for" in line:
            usr = ParseUsr(line)

            # add 'em if they don't exist
            if usr not in logs:
                logs[usr] = Log(usr)

            ip = ParseIP(line)
            # set info
            if ip not in logs[usr].ips:
                logs[usr].ips.append(ip)
            logs[usr].succ_logs.append(line.rstrip('\n'))
            logs[usr].logs.append(line.rstrip('\n'))

        # match a failed login
        elif "Failed password for" in line:
            # parse user
            usr = ParseUsr(line)

            if usr not in logs:
                logs[usr] = Log(usr)

            ip = ParseIP(line)

            if ip not in logs[usr].ips:
                logs[usr].ips.append(ip)
            logs[usr].fail_logs.append(line.rstrip('\n'))
            logs[usr].logs.append(line.rstrip('\n'))

        # match failed auth
        elif ":auth): authentication failure;" in line:
            # so there are three flavors of authfail we care about;
            # su, sudo, and ssh.  Lets parse each.
            usr = re.search(r'(\blogname=)(\w+)', line)
            if usr is not None:
                usr = usr.group(2)
            # parse a fail log to ssh
            if "(sshd:auth)" in line:
                # ssh doesn't have a logname hurr
                usr = ParseUsr(line)
                if usr not in logs:
                    logs[usr] = Log(usr)
                logs[usr].ips.append(ParseIP(line))
            # parse sudo/su fails
            else:
                if usr not in logs:
                    logs[usr] = Log(usr)
            logs[usr].fail_logs.append(line.rstrip('\n'))
            logs[usr].logs.append(line.rstrip('\n'))
        # match commands
        elif "sudo:" in line:
            # parse user
            usr = ParseUsr(line)
            if usr not in logs:
                logs[usr] = Log(usr)

            cmd = ParseCmd(line)
            # append the command if it isn't there already
            if cmd is not None:
                if cmd not in logs[usr].commands:
                    logs[usr].commands.append(cmd)
            logs[usr].logs.append(line.rstrip('\n'))
    return logs