1# -*- coding: utf-8 -*-
2# Copyright (C) 2018-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
19import sys
20from argparse import ArgumentParser, Namespace, RawTextHelpFormatter
21from gvm.protocols.gmp import Gmp
22
23from gvmtools.helper import create_xml_tree, error_and_exit, yes_or_no
24
25HELP_TEXT = """
26        This script pulls tasks data from an xml document and feeds it to \
27            a desired GSM
28        Usage examples:
29            $ gvm-script --gmp-username name --gmp-password pass ssh --hostname
30            ... send-task.gmp.py +h
31            ... send-task.gmp.py ++x xml_file
32    """
33
34
35def numerical_option(statement, list_range):
36    choice = int(input(statement))
37
38    if choice in range(1, list_range + 1):
39        return choice
40    else:
41        return numerical_option(
42            f"Please enter valid number from 1 to {list_range}...",
43            list_range,
44        )
45
46
47def interactive_options(gmp, task, keywords):
48    options_dict = {}
49    options_dict['config'] = gmp.get_scan_configs()
50    options_dict['scanner'] = gmp.get_scanners()
51    options_dict['target'] = gmp.get_targets()
52
53    for option_key, option_value in options_dict.items():
54        object_dict, object_list = {}, []
55        object_id = task.find(option_key).get('id')
56        object_xml = option_value
57
58        for i in object_xml.findall(option_key):
59            object_dict[i.find('name').text] = i.xpath('@id')[0]
60            object_list.append(i.find('name').text)
61
62        if object_id in object_dict.values():
63            keywords[f'{option_key}_id'] = object_id
64        elif object_id not in object_dict.values() and len(object_dict) != 0:
65            response = yes_or_no(
66                f"\nRequired Field: failed to detect {option_key}_id: "
67                f"{task.xpath(f'{option_key}/@id')[0]}... "
68                "\nWould you like to select from available options, or exit "
69                "the script?"
70            )
71
72            if response is True:
73                counter = 1
74                print(f"{option_key.capitalize()} options:")
75                for j in object_list:
76                    print(f"    {counter} - {j}")
77                    counter += 1
78                answer = numerical_option(
79                    "\nPlease enter the number of your choice.",
80                    len(object_list),
81                )
82                keywords[f'{option_key}_id'] = object_dict[
83                    object_list[answer - 1]
84                ]
85            else:
86                print("\nTerminating...")
87                sys.exit()
88        else:
89            error_and_exit(
90                f"Failed to detect {option_key}_id\nThis field is required "
91                "therefore the script is unable to continue.\n"
92            )
93
94
95def parse_send_xml_tree(gmp, xml_tree):
96    task_xml_elements = xml_tree.xpath('task')
97    print(task_xml_elements)
98    if not task_xml_elements:
99        error_and_exit("No tasks found.")
100    tasks = []
101    for task in task_xml_elements:
102        keywords = {'name': task.find('name').text}
103
104        if task.find('comment').text is not None:
105            keywords['comment'] = task.find('comment').text
106
107        interactive_options(gmp, task, keywords)
108
109        if task.find('schedule_periods') is not None:
110            keywords['schedule_periods'] = int(
111                task.find('schedule_periods').text
112            )
113
114        if task.find('observers').text:
115            keywords['observers'] = task.find('observers').text
116
117        if task.xpath('schedule/@id')[0]:
118            keywords['schedule_id'] = task.xpath('schedule/@id')[0]
119
120        if task.xpath('preferences/preference'):
121            preferences, scanner_name_list, value_list = {}, [], []
122
123            for preference in task.xpath('preferences/preference'):
124                scanner_name_list.append(preference.find('scanner_name').text)
125                if preference.find('value').text is not None:
126                    value_list.append(preference.find('value').text)
127                else:
128                    value_list.append('')
129            preferences['scanner_name'] = scanner_name_list
130            preferences['value'] = value_list
131            keywords['preferences'] = preferences
132
133        new_task = gmp.create_task(**keywords)
134
135        tasks.append(new_task.xpath('//@id')[0])
136    return tasks
137
138
139def main(gmp: Gmp, args: Namespace) -> None:
140    # pylint: disable=undefined-variable, unused-argument
141
142    parser = ArgumentParser(
143        prefix_chars="+",
144        add_help=False,
145        formatter_class=RawTextHelpFormatter,
146        description=HELP_TEXT,
147    )
148
149    parser.add_argument(
150        "+h",
151        "++help",
152        action="help",
153        help="Show this help message and exit.",
154    )
155
156    parser.add_argument(
157        "+x",
158        "++xml-file",
159        dest='xml',
160        type=str,
161        required=True,
162        help='xml file containing tasks',
163    )
164
165    script_args, _ = parser.parse_known_args()
166
167    # check_args(args)
168
169    print('\nSending task(s)...')
170
171    xml_tree = create_xml_tree(script_args.xml)
172    tasks = parse_send_xml_tree(gmp, xml_tree)
173    for task in tasks:
174        print(task)
175    print('\nTask(s) sent!\n')
176
177
178if __name__ == '__gmp__':
179    main(gmp, args)  # pylint: disable=undefined-variable
180