#
#   logactio  -  simple framework for doing configured action on certain
#                log file events
#
# SPDX-FileCopyrightText: Copyright 2012-  David Sommerseth <dazo@eurephia.org>
# SPDX-License-Identifier: GPL-2.0-only
#

import sys
import smtplib
from email.mime.text import MIMEText
from LogActio import Message, ReporterQueue


class SMTPreporter(ReporterQueue.ReporterQueue):
    """Simple LogActio reporter module, sending alerts via SMTP

    Example configuration to be used in /etc/logactio.cfg

        [Reporter:SMTP]
        module: SMTPreporter
        sender: logactio@example.com
        recipients: john.doe@example.com, jane.doe@example.com
        subject_prefix: LogActio Alert:
        smtp_host: localhost
        smtp_port: 25

    This will send reports to the configured 'recipients' using the given SMTP
    server.
    """

    def __init__(self, config, logger=None):
        if "sender" not in config:
            raise Exception("SMTPreporter is not configured with a sender address")
        if "recipients" not in config:
            raise Exception("SMTPreporter is not configured with any recipients")
        if "smtp_host" not in config:
            raise Exception("SMTPreporter is not configured with SMTP host")

        self.__log = logger if logger is not None else self.__logfnc
        self.__sender = config["sender"]
        self.__recipients = config["recipients"].split(",")
        self.__subjprefix = config.get("subject_prefix", None)
        self.__smtphost = config["smtp_host"]
        self.__smtpport = int(config["smtp_port"]) if "smtp_port" in config else 25
        self.__smtpuser = config.get("smtp_username", None)
        self.__smtppass = config.get("smtp_password", None)
        self.__sslmode = config.get("sslmode", None)
        self.__idx = 0

        if (self.__smtpuser and not self.__smtppass) or (not self.__smtpuser and self.__smtppass):
            raise Exception("SMTPreporter must have both smtp_username and smtp_password")

        if self.__sslmode is not None and self.__sslmode not in ("SSL", "STARTTLS"):
            raise Exception("SMTPreporter accepts only SSL or STARTTLS as sslmode values")

        super().__init__("SMTPreporter", "SMTP Reporter", self.__processqueue)


    def __logfnc(self, lvl, msg):
        print(msg)
        sys.stdout.flush()


    def __processqueue(self):
        done = False

        # Process the message queue
        while not done:
            msg = self._QueueGet()

            if msg.MessageType() == Message.MSG_SHUTDOWN:
                done = True

            elif msg.MessageType() == Message.MSG_SEND:
                m = msg.Message()

                mail = MIMEText(m["body"])
                mail["From"] = self.__sender
                mail["To"] = ",".join(self.__recipients)
                if self.__subjprefix is None:
                    mail["Subject"] = f"LogActio Alert: {m['subject']}"
                else:
                    mail["Subject"] = f"{self.__subjprefix} {m['subject']}"

                try:
                    self.__log(3, f"[SMTPreporter] Connecting to {self.__smtphost}:{self.__smtpport}")

                    if self.__sslmode == "SSL":
                        smtp = smtplib.SMTP_SSL(self.__smtphost, self.__smtpport)
                    else:
                        smtp = smtplib.SMTP(self.__smtphost, self.__smtpport)
                        if self.__sslmode == "STARTTLS":
                            smtp.starttls()

                    if self.__smtpuser is not None:
                        self.__log(3, f"[SMTPreporter] Logging in as {self.__smtpuser}")
                        smtp.login(self.__smtpuser, self.__smtppass)

                    smtp.sendmail(self.__sender, self.__recipients, mail.as_string())
                    self.__idx += 1
                    self.__log(1, f"[SMTPReporter] {{{self.__idx}}} {{Rule {m['rulename']}}} "
                                  f"Logfile: {m['logfile']}, Threshold: {m['threshold']}, "
                                  f"Count: {m['count']} -- Report sent to: {', '.join(self.__recipients)}")
                    self.__log(2, f"[SMTPReporter] {{{self.__idx}}} Details: {m['msg']}")
                    smtp.quit()
                except Exception as e:
                    self.__log(0, f"** ERROR ** SMTPreporter failed: {{{self.__idx}}} {e}")


    def ProcessEvent(self, logfile, rulename, msg, count, threshold):
        # Format the report message
        msg = {"rulename": rulename, "threshold": threshold,
               "count": count, "logfile": logfile, "msg": msg,
               "subject": f"Alerts from {logfile}",
               "body": f"\nLogActio have detected {count} events in the {logfile} log file "
                       f"since it started running.\n\n"
                       f"The following information was extracted:\n\n"
                       f"{chr(10).join(msg)}\n"}

        # Queue the message for sending
        self._QueueMsg(0, msg)


def InitReporter(config, logger=None):
    return SMTPreporter(config, logger)
