#!/usr/bin/env python3

import psycopg2, json, os, datetime, requests, slack, smtplib, csv, logging
from docx import Document
from jira.client import JIRA
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from defectdojo_api import defectdojo

File_Dir = os.path.dirname(os.path.realpath('__file__'))
Configuration_File = os.path.join(File_Dir, 'plugins/common/config/config.json')

def Date():
    return str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

def Load_Chrome_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading RTIR configuration data.")

    try:
        with open(Configuration_File) as JSON_File:
            Configuration_Data = json.load(JSON_File)
            GC_Details = Configuration_Data['google-chrome']
            GC_App_Path = GC_Details['application-path']
            GC_Drv_Path = GC_Details['chromedriver-path']

            if GC_App_Path and GC_Drv_Path:
                return [GC_App_Path, GC_Drv_Path]

            else:
                return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Load_CSV_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading CSV configuration data.")

    try:
        with open(Configuration_File) as JSON_File:
            Configuration_Data = json.load(JSON_File)
            CSV_Details = Configuration_Data['csv']
            Use_CSV = CSV_Details['use-csv']

            if Use_CSV:
                return Use_CSV

            else:
                return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Load_DOCX_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading DOCX configuration data.")

    try:
        with open(Configuration_File) as JSON_File:
            Configuration_Data = json.load(JSON_File)
            DOCX_Details = Configuration_Data['docx-report']
            Use_DOCX = DOCX_Details['use-docx']

            if Use_DOCX:
                return Use_DOCX

            else:
                return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Load_Defect_Dojo_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading DefectDojo configuration data.")

    try:
        with open(Configuration_File) as JSON_File:
            Configuration_Data = json.load(JSON_File)
            DD_Details = Configuration_Data['defectdojo']
            DD_API_Key = DD_Details['api_key']
            DD_Host = DD_Details['host']
            DD_User = DD_Details['user']
            DD_Engagement_ID = DD_Details['engagement-id']
            DD_Product_ID = DD_Details['product-id']
            DD_Test_ID = DD_Details['test-id']
            DD_User_ID = DD_Details['user-id']

        if DD_API_Key and DD_Host and DD_User and DD_Engagement_ID and DD_Product_ID and DD_Test_ID and DD_User_ID:
            return [DD_API_Key, DD_Host, DD_User, DD_Engagement_ID, DD_Product_ID, DD_Test_ID, DD_User_ID]

        else:
            return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Load_Email_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading email configuration data.")

    try:
        with open(Configuration_File) as JSON_File:
            Configuration_Data = json.load(JSON_File)
            Email_Details = Configuration_Data['email']
            Email_SMTP_Server = Email_Details['smtp_server']
            Email_SMTP_Port = int(Email_Details['smtp_port'])
            Email_From_Address = Email_Details['from_address']
            Email_From_Password = Email_Details['from_password']
            Email_To_Address = Email_Details['to_address']

        if Email_SMTP_Server and Email_SMTP_Port and Email_From_Address and Email_From_Password and Email_To_Address:
            return [Email_SMTP_Server, Email_SMTP_Port, Email_From_Address, Email_From_Password, Email_To_Address]

        else:
            return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Load_Elasticsearch_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading Elasticsearch configuration data.")

    try:
        with open(Configuration_File) as JSON_File:
            Configuration_Data = json.load(JSON_File)
            Elasticsearch_Details = Configuration_Data['elasticsearch']
            Elasticsearch_Service = Elasticsearch_Details['service']
            Elasticsearch_Host = Elasticsearch_Details['host']
            Elasticsearch_Port = int(Elasticsearch_Details['port'])

        if Elasticsearch_Service and Elasticsearch_Host and Elasticsearch_Port:
            return [Elasticsearch_Service, Elasticsearch_Host, Elasticsearch_Port]

        else:
            return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Load_Main_Database():
    logging.info(f"{Date()} Connectors Library - Loading Scrummage's Main Database configuration data.")

    try:
        with open(Configuration_File) as JSON_File:
            Configuration_Data = json.load(JSON_File)
            DB_Info = Configuration_Data['postgresql']
            DB_Host = DB_Info['host']
            DB_Port = str(int(DB_Info['port']))
            DB_Username = DB_Info['user']
            DB_Password = DB_Info['password']
            DB_Database = DB_Info['database']

    except:
        logging.warning(f"{Date()} Connectors Library - Failed to load configuration file.")

    try:
        DB_Connection = psycopg2.connect(user=DB_Username,
                                      password=DB_Password,
                                      host=DB_Host,
                                      port=DB_Port,
                                      database=DB_Database)

        if DB_Connection:
            return DB_Connection

        else:
            return None

    except:
        logging.warning(f"{Date()} Connectors Library - Failed to connect to database.")

def Load_JIRA_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading JIRA configuration data.")

    try:

        with open(Configuration_File) as json_file:  
            Configuration_Data = json.load(json_file)
            JSON_Details = Configuration_Data['JIRA']
            JIRA_Project_Key = JSON_Details['project_key']
            JIRA_Address = JSON_Details['address']
            JIRA_Username = JSON_Details['username']
            JIRA_Password = JSON_Details['password']
            JIRA_Ticket_Type = JSON_Details['ticket_type']

            if JIRA_Project_Key and JIRA_Address and JIRA_Username and JIRA_Password and JIRA_Ticket_Type:
                return [JIRA_Project_Key, JIRA_Address, JIRA_Username, JIRA_Password, JIRA_Ticket_Type]

            else:
                return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Load_Slack_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading Slack configuration data.")

    try:

        with open(Configuration_File) as json_file:  
            Configuration_Data = json.load(json_file)
            JSON_Details = Configuration_Data['slack']
            Slack_Token = JSON_Details['token']
            Slack_Channel = JSON_Details['channel']

            if Slack_Token and Slack_Channel:
                return [Slack_Token, Slack_Channel]

            else:
                return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Load_Scumblr_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading Scumblr configuration data.")

    try:

        with open(Configuration_File) as JSON_File:
            Configuration_Data = json.load(JSON_File)
            PostgreSQL_Details = Configuration_Data['scumblr']
            PostgreSQL_Host = PostgreSQL_Details['host']
            PostgreSQL_Port = str(PostgreSQL_Details['port'])
            PostgreSQL_Database = PostgreSQL_Details['database']
            PostgreSQL_User = PostgreSQL_Details['user']
            PostgreSQL_Password = PostgreSQL_Details['password']

            if PostgreSQL_Host and PostgreSQL_Port and PostgreSQL_Database and PostgreSQL_User and PostgreSQL_Password:
                return [PostgreSQL_Host, PostgreSQL_Port, PostgreSQL_Database, PostgreSQL_User, PostgreSQL_Password]

            else:
                return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Load_RTIR_Configuration():
    logging.info(f"{Date()} Connectors Library - Loading RTIR configuration data.")

    try:
        with open(Configuration_File) as JSON_File:
            Configuration_Data = json.load(JSON_File)
            RTIR_Details = Configuration_Data['rtir']
            RTIR_HTTP_Service  = RTIR_Details['service']
            RTIR_Host = RTIR_Details['host']
            RTIR_Port = str(RTIR_Details['port'])
            RTIR_User = RTIR_Details['user']
            RTIR_Password = RTIR_Details['password']
            RTIR_Authenticator = RTIR_Details['authenticator']

            if RTIR_HTTP_Service and RTIR_Host and RTIR_Port and RTIR_User and RTIR_Password and RTIR_Authenticator:
                return [RTIR_Host, RTIR_Port, RTIR_User, RTIR_Password, RTIR_HTTP_Service, RTIR_Authenticator]

            else:
                return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def CSV_Output(Title, Plugin_Name, Domain, Link, Result_Type, Output_File, Task_ID, Directory):

    try:
        Use_CSV = Load_CSV_Configuration()

        if Use_CSV:
            Headings = ["Title", "Plugin", "Domain", "Link", "Created At", "Output Files", "Result Type", "Task ID"]
            Data = [Title, Plugin_Name, Domain, Link, Date(), Output_File, Result_Type, str(Task_ID)]
            Complete_File = f"{File_Dir}/static/protected/output/{Directory}/{Plugin_Name}-Output.csv"

            if not os.path.exists(Complete_File):
                CSV_Output = csv.writer(open(Complete_File, 'w'))
                CSV_Output.writerow(Headings)
                CSV_Output.writerow(Data)
                logging.info(f"{Date()} Connectors Library - Created new CSV file located at {str(Complete_File)}.")

            else:
                CSV_Output = csv.writer(open(Complete_File, 'a'))
                CSV_Output.writerow(Data)
                logging.info(f"{Date()} Connectors Library - Updated existing CSV file located at {str(Complete_File)}.")

            return Complete_File

        else:
            return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def DOCX_Output(Title, Plugin_Name, Domain, Link, Result_Type, Output_File, Task_ID, Directory):

    try:
        Use_DOCX = Load_DOCX_Configuration()

        if Use_DOCX:
            Complete_File = f"{File_Dir}/static/protected/output/{Directory}/{Plugin_Name}-Output.docx"

            if os.path.exists(Complete_File):
                document = Document(Complete_File)

            else:
                document = Document()

            Document_Data = (
                ('Plugin', Plugin_Name),
                ('Domain', Domain),
                ('Link', Link),
                ('Created At', str(Date())),
                ('Result Type', Result_Type),
                ('Output Files', Output_File),
                ('Associated Task ID', str(Task_ID))
            )

            table = document.add_table(rows=1, cols=2)
            hdr_cells = table.rows[0].cells
            hdr_cells[0].text = 'Title'
            hdr_cells[1].text = Title

            for name, data in Document_Data:
                row_cells = table.add_row().cells
                row_cells[0].text = name
                row_cells[1].text = data

            document.add_page_break()
            document.save(Complete_File)
            logging.info(f"{Date()} Connectors Library - Exported to DOCX file located at {str(Complete_File)}.")
            return Complete_File

        else:
            return None

    except Exception as e:
        logging.warning(f"{Date()} Connectors Library - {str(e)}.")


def Defect_Dojo_Output(Title, Description):
    DD_Details = Load_Defect_Dojo_Configuration()

    if DD_Details:

        try:
            Impact = 'All Scrummage findings have the potential to cause significant damage to a business\' finances, efficiency and reputation. Therefore, findings should be investigated to assist in reducing this risk.'
            Mitigation = 'It is recommended that this issue be investigated further by the security team to determine whether or not further action needs to be taken.'
            DD_Connection = defectdojo.DefectDojoAPI(DD_Details[1], DD_Details[0], DD_Details[2], debug=False)
            Finding = DD_Connection.create_finding(Title, Description, 'Low', '', str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d')), DD_Details[4], DD_Details[3], DD_Details[5], DD_Details[6], Impact, True, False, Mitigation)

            try:
                Finding = str(int(str(Finding)))
                logging.info(f"{Date()} Connectors Library - DefectDojo finding {Finding} created.")

            except:
                logging.info(f"{Date()} Connectors Library - Failed to create DefectDojo finding.")

        except (Exception, psycopg2.DatabaseError) as Error:
            logging.warning(Date() + str(Error))

def Main_Database_Insert(Title, Plugin_Name, Domain, Link, Result_Type, Output_File, Task_ID):
    Connection = Load_Main_Database()

    if Connection:

        try:
            # Create connection cursor.
            Cursor = Connection.cursor()
            Cursor.execute("SELECT * FROM results WHERE link like %s", (Link,))
            Item_Already_in_Database = Cursor.fetchone()

            if Item_Already_in_Database is None:
                # Execute statement.
                Cursor.execute("INSERT INTO results (title, plugin, status, domain, link, created_at, updated_at, output_file, result_type, task_id) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)", (Title, Plugin_Name, "Open", Domain, Link, Date(), Date(), Output_File, Result_Type, Task_ID,))

            else:
                logging.info(f"{Date()} Connectors Library - Entry already exists in the database. Skipping...")

        except (Exception, psycopg2.DatabaseError) as Error:
            logging.warning(Date() + str(Error))

        finally:

            if Connection is not None:
                Connection.commit()
                Connection.close()

def Scumblr_Main(Link, Domain, Title):
    Scumblr_Details = Load_Scumblr_Configuration()
    Connection = ""

    if Scumblr_Details:

        try:
            # Connect to the PostgreSQL server.
            Connection = psycopg2.connect(host=Scumblr_Details[0], port=Scumblr_Details[1], database=Scumblr_Details[2], user=Scumblr_Details[3], password=Scumblr_Details[4])

            # Create connection cursor.
            Cursor = Connection.cursor()
            Cursor.execute("SELECT * FROM results WHERE url like %s", (Link,))
            Item_Already_in_Database = Cursor.fetchone()

            if Item_Already_in_Database is None:
                # Execute statement.
                Cursor.execute("INSERT INTO results (title, url, created_at, updated_at, domain) VALUES(%s, %s, %s, %s, %s)", (Title, Link, Date(), Date(), Domain))

            else:
                logging.info(f"{Date()} Connectors Library - Entry already exists in Scumblr database. Skipping...")

        except (Exception, psycopg2.DatabaseError) as Error:
            logging.warning(f"{Date()} Connectors Library - " + Error)

        finally:

            if Connection is not None:
                Connection.commit()
                Connection.close()
                logging.info(f"{Date()} Connectors Library - Result added to Scumblr database.")
                logging.info(f"{Date()} Connectors Library - Database connection closed.")

def RTIR_Main(Ticket_Subject, Ticket_Text):
    RTIR_Details = Load_RTIR_Configuration()

    if RTIR_Details:

        try:
            Request_Data = "content=id: ticket/new\nQueue: 1\nSubject: " + Ticket_Subject + "\nText: " + Ticket_Text

            if RTIR_Details[5] == "cookie_based":
                requests.post(RTIR_Details[4] + '://' + RTIR_Details[0] + ':' + RTIR_Details[1] + '/REST/1.0/ticket/new?user=' + RTIR_Details[2] + "&pass=" + RTIR_Details[3], Request_Data)

            else:
                logging.info(f"{Date()} Connectors Library - No Authenticator specified, using the default which is cookie-based authentication,")
                requests.post(RTIR_Details[4] + '://' + RTIR_Details[0] + ':' + RTIR_Details[1] + '/REST/1.0/ticket/new?user=' + RTIR_Details[2] + "&pass=" + RTIR_Details[3], Request_Data)

            logging.info(f"{Date()} Connectors Library - New RTIR ticket created.")

        except Exception as e:
            logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def JIRA_Main(Ticket_Summary, Ticket_Description):
    JIRA_Details = Load_JIRA_Configuration()

    if JIRA_Details:

        try:
            JIRA_Options={'server': JIRA_Details[1]}
            JIRA_Session=JIRA(options=JIRA_Options,basic_auth=(JIRA_Details[2], JIRA_Details[3]))
            JIRA_Session.create_issue(project={'key': JIRA_Details[0]}, summary=Ticket_Summary, description=Ticket_Description, issuetype={'name': JIRA_Details[4]})
            logging.info(f"{Date()} Connectors Library - New JIRA ticket created.")

        except Exception as e:
            logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Slack_Main(Description):
    Slack_Details = Load_Slack_Configuration()

    if Slack_Details:

        try:
            client = slack.WebClient(token=Slack_Details[0])
            client.chat_postMessage(channel=Slack_Details[1], text=Description)
            logging.info(f"{Date()} Connectors Library - New Slack Notification created.")

        except Exception as e:
            logging.warning(f"{Date()} Connectors Library - {str(e)}.")

def Elasticsearch_Main(Title, Plugin_Name, Domain, Link, Result_Type, Output_File, Task_ID, Concat_Plugin_Name):
    Elasticsearch_Details = Load_Elasticsearch_Configuration()

    if Elasticsearch_Details:

        try:
            URI = Elasticsearch_Details[0] + Elasticsearch_Details[1] + ":" + str(Elasticsearch_Details[2]) + "/scrummage/result/" + Concat_Plugin_Name
            headers = {"Content-Type": "application/json"}
            data = {"title": Title, "plugin": Plugin_Name, "domain": Domain, "link": Link, "output_file": Output_File, "result_type": Result_Type, "created_at": Date(), "associated_task_id": str(Task_ID)}
            data = json.dumps(data)
            resp = requests.post(URI, data=data, headers=headers)

            if resp.status_code == 200:
                logging.info(f"{Date()} Connectors Library - New result created in Elasticsearch, using the URI " + URI + ".")

            else:
                logging.info(f"{Date()} Connectors Library - Failed to create result in Elasticsearch, using the URI " + URI + ".")

        except:
            logging.warning(f"{Date()} Connectors Library - Failed to create result in Elasticsearch.")

def Email_Main(Email_Subject, Email_Body):
    Email_Details = Load_Email_Configuration()

    if Email_Details:

        try: # Send Email Alerts when called.
            server = smtplib.SMTP(Email_Details[0], Email_Details[1])
            server.ehlo()
            server.starttls()
            server.login(Email_Details[2], Email_Details[3])
            msg = MIMEMultipart()
            msg['From'] = Email_Details[2]
            msg['To'] = Email_Details[4]
            msg['Subject'] = Email_Subject
            msg.attach(MIMEText(Email_Body, 'plain'))
            text = msg.as_string()
            server.sendmail(Email_Details[2], Email_Details[4], text)
            server.quit()
            logging.info(f"{Date()} Connectors Library - Email Sent.")

        except:
            logging.warning(f"{Date()} Connectors Library - Failed to send alert! Check email login settings.")