1# -*- coding: utf-8 -*-
2# Copyright (C) 2017-2021 Greenbone Networks GmbH
3#
4# SPDX-License-Identifier: GPL-3.0-or-later
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19# GMP script for gvm-pyshell to send emails with delta reports.
20
21import base64
22import datetime
23import sched
24import smtplib
25import sys
26import time
27
28from email.mime.base import MIMEBase
29from email.mime.multipart import MIMEMultipart
30from email.utils import formatdate
31
32from argparse import Namespace
33from gvm.protocols.gmp import Gmp
34
35
36def check_args(args: Namespace) -> None:
37    len_args = len(args.script) - 1
38    if len_args != 0:
39        message = """
40        This script, once started, will continuously send delta
41        reports via email for selected tasks.
42
43        Example for starting up the routine:
44            $ gvm-script --gmp-username name --gmp-password pass ssh --hostname <gsm> scripts/send-delta-emails.gmp.py
45
46        The routine follows this procedure:
47
48        Every <interval> minutes do:
49        Get all tasks where the tag <task_tag> is attached.
50        For each of these tasks get the finished reports:
51            If less than 2 reports, continue with next task
52            If latest report has tag "delta_alert_sent", continue with next task
53            Create a CSV report from the delta of latest vs. previous report
54            where filtered for only the new results.
55            Send the CSV as an attachment to the configured email address.
56        """
57        print(message)
58        sys.exit()
59
60
61def execute_send_delta_emails(sc: sched.scheduler, **kwargs: dict) -> None:
62    gmp = kwargs.get('gmp')
63    task_tag = kwargs.get('task_tag')
64    interval = kwargs.get('interval')
65    email_subject = kwargs.get('email_subject')
66    to_addresses = kwargs.get('to_addresses')
67    from_address = kwargs.get('from_address')
68    mta_address = kwargs.get('mta_address')
69    mta_user = kwargs.get('mta_user')
70    mta_port = kwargs.get('mta_port')
71    mta_password = kwargs.get('mta_password')
72    report_tag_name = kwargs.get('report_tag_name')
73
74    print('Retrieving task list ...')
75
76    task_filter = f'tag={task_tag}'
77    tasks = gmp.get_tasks(filter_string=task_filter).xpath('task')
78    print(f'Found {str(len(tasks))} task(s) with tag "{task_tag}".')
79
80    for task in tasks:
81        task_id = task.xpath('@id')[0]
82        task_name = task.xpath('name/text()')[0]
83        print(f'Processing task "{task_name}" ({task_id})...')
84
85        reports = gmp.get_reports(
86            filter_string='task_id={task_id} and status=Done '
87            'sort-reverse=date'
88        ).xpath('report')
89        print(f'  Found {str(len(reports))} report(s).')
90        if len(reports) < 2:
91            print('  Delta-reporting requires at least 2 finished reports.')
92            continue
93
94        if reports[0].xpath(
95            'report/user_tags/tag/' 'name[text()="delta_alert_sent"]'
96        ):
97            print('  Delta report for latest finished report already sent')
98            continue
99
100        print(
101            '  Latest finished report not send yet. Preparing delta '
102            'report...'
103        )
104
105        delta_report = gmp.get_report(
106            report_id=reports[0].xpath('@id')[0],
107            delta_report_id=reports[1].xpath('@id')[0],
108            filter_string='delta_states=n',
109            format_id='c1645568-627a-11e3-a660-406186ea4fc5',
110        )
111
112        csv_in_b64 = delta_report.xpath('report/text()')[0]
113        csv = base64.b64decode(csv_in_b64)
114
115        print("  Composing Email...")
116        alert_email = MIMEMultipart()
117        alert_email['Subject'] = email_subject
118        alert_email['To'] = ', '.join(to_addresses)
119        alert_email['From'] = from_address
120        alert_email['Date'] = formatdate(localtime=True)
121
122        report_attachment = MIMEBase('application', "octet-stream")
123        report_attachment.add_header(
124            'Content-Disposition', 'attachment', filename='delta.csv'
125        )
126        report_attachment.set_payload(csv)
127        alert_email.attach(report_attachment)
128
129        print("  Sending Email...")
130        try:
131            with smtplib.SMTP(mta_address, mta_port) as smtp:
132                smtp.ehlo()
133                smtp.starttls()
134                smtp.ehlo()
135                smtp.login(mta_user, mta_password)  # if required
136                smtp.sendmail(
137                    from_address, to_addresses, alert_email.as_string()
138                )
139                smtp.close()
140                print("  Email has been sent!")
141
142                gmp.create_tag(
143                    name=report_tag_name,
144                    resource_id=reports[0].xpath('@id')[0],
145                    resource_type='report',
146                    value=datetime.datetime.now(),
147                )
148        except Exception:  # pylint: disable=broad-except
149            print("  Unable to send the email. Error: ", sys.exc_info()[0])
150            # raise # in case an error should stop the script
151            continue  # ignore the problem for the time being
152
153    print(f"\nCheck will be repeated in {str(interval)} minutes...\n")
154    sc.enter(
155        interval * 60,
156        1,
157        execute_send_delta_emails,
158        argument=(sc,),
159        kwargs=kwargs,
160    )
161
162
163def main(gmp: Gmp, args: Namespace) -> None:
164    # pylint: disable=undefined-variable
165
166    check_args(args)
167
168    interval = 1  # in minutes
169    task_tag = 'send_delta_alert'
170    report_tag_name = 'delta_alert_sent'
171    email_subject = 'Delta Report'
172    from_address = 'admin@example.com'
173    to_addresses = ['user1@example.com', 'user2@example.com']
174    mta_address = 'mail.example.com'
175    mta_port = 25
176    mta_user = 'admin@example.com'
177    mta_password = 'mysecret'
178
179    print('send_delta_alerts starting up with following settings:')
180    print(f'User:          {args.username}')
181    print(f'Interval:      {str(interval)} minutes')
182    print(f'Task tag:      {task_tag}')
183    print(f'Email subject: {email_subject}')
184    print(f'From Address:  {from_address}')
185    print(f'To Addresses:  {to_addresses}')
186    print(f'MTA Address:   {mta_address}')
187    print(f'MTA Port:      {str(mta_port)}')
188    print(f'MTA User:      {mta_user}')
189    print('MTA Password:  <will not be printed here>')
190    print()
191
192    print(f'Entering loop with interval {str(interval)} minutes ...')
193
194    schedule = sched.scheduler(time.time, time.sleep)
195
196    # Enter the scheduled execution with the given interval
197    schedule.enter(
198        0,
199        1,
200        execute_send_delta_emails,
201        argument=(schedule,),
202        kwargs={
203            'gmp': gmp,
204            'task_tag': task_tag,
205            'interval': interval,
206            'email_subject': email_subject,
207            'to_addresses': to_addresses,
208            'from_address': from_address,
209            'mta_address': mta_address,
210            'mta_password': mta_password,
211            'mta_port': mta_port,
212            'mta_user': mta_user,
213            'report_tag_name': report_tag_name,
214        },
215    )
216    schedule.run()
217
218
219if __name__ == '__gmp__':
220    main(gmp, args)
221