"""
vuln_add.py

Copyright 2013 Andres Riancho

This file is part of w3af, http://w3af.org/ .

w3af is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.

w3af is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

"""
import gtk

from w3af.core.data.kb.vuln_templates.utils import (get_template_names,
                                                    get_template_long_names,
                                                    get_template_by_name)
from w3af.core.ui.gui.confpanel import ConfigDialog, OnlyOptions
from w3af.core.ui.gui.constants import W3AF_ICON


class VulnAddDialog(gtk.Dialog):
    """Ask for vulnerability information and store the new vuln in the KB

    :author: Andres Riancho
    """
    def __init__(self, w3af):
        super(VulnAddDialog, self).__init__("Add new vulnerability", None,
                                            gtk.MESSAGE_QUESTION,
                                            (gtk.STOCK_CANCEL,
                                             gtk.RESPONSE_CANCEL,
                                             gtk.STOCK_ADD,
                                             gtk.RESPONSE_OK))
        self.set_icon_from_file(W3AF_ICON)
        
        self.w3af = w3af
        
        # Main hbox
        hbox = gtk.HBox()
        hbox.show()
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG)
        image.set_padding(20, 20)
        image.show()
        
        hbox.pack_start(image)
        
        # Vbox with all the information for the right section of the dialog
        vbox = gtk.VBox()
        
        # Add a label
        align = gtk.Alignment()
        align.set_padding(10, 0, 0, 10)
        label = gtk.Label('Choose the vulnerability template to use:')
        align.add(label)
        vbox.pack_start(align)
        
        template_long_names = get_template_long_names()
        template_names = get_template_names()
        self.vuln_template = None
        
        # A list store with the following columns:
        #    * Long template name (show)
        #    * Template name (code internals)
        liststore = gtk.ListStore(str, str)
        self.combobox = gtk.ComboBox(liststore)
        cell = gtk.CellRendererText()
        self.combobox.pack_start(cell, True)
        self.combobox.add_attribute(cell, 'text', 0)
        self.combobox.connect("changed", self._changed_combo)
        
        for i, long_name in enumerate(template_long_names):
            liststore.append((long_name, template_names[i]))
        
        vbox.pack_start(self.combobox, False, False)
        vbox.pack_start(gtk.Label())
        
        # the Cancel button
        but = self.action_area.get_children()[1]
        but.connect("clicked", lambda x: self.destroy())
        self.connect("delete-event", lambda x, y: self.destroy())
        
        # the Ok button
        self.ok_but = self.action_area.get_children()[0]
        self.ok_but.connect("clicked", self._ok)
        self.ok_but.set_sensitive(False)
        
        hbox.pack_start(vbox)
        
        self.vbox.pack_start(hbox)
        self.show_all()

    def _ok(self, w):
        """Collects the information and stores it in KB"""
        self.destroy()
        
        if self.vuln_template is not None:
            TemplateConfigDialog(_("Vulnerability parameters"), self.w3af,
                                 self.vuln_template)
    
    def _changed_combo(self, *args):
        """Show the correct config panel according to the combo selection"""
        self.vuln_template = vuln_template = self._get_active_template_in_combo()
        self.ok_but.set_sensitive(True)
        
        if vuln_template is None:
            self.ok_but.set_sensitive(False)
            return
    
    def _get_active_template_in_combo(self):
        """
        Based on the user selection in the combo, return a template instance or
        None if there was any type of error.
        """
        model = self.combobox.get_model()
        active = self.combobox.get_active()
        if active < 0:
            return None
        
        try:
            template = get_template_by_name(model[active][1])
        except:
            return None
        
        return template


class TemplateConfigDialog(ConfigDialog):
    """Puts a Config panel inside a Dialog, subclassed for Template objects.

    :param title: the title of the window.
    :param w3af: the Core instance
    :param template: the template to configure

    :author: Andres Riancho
    """
    def __init__(self, title, w3af, template):
        # pylint: disable=E1003
        super(ConfigDialog, self).__init__(title, None, gtk.DIALOG_MODAL, ())
        self.set_icon_from_file(W3AF_ICON)

        # buttons and config panel
        save_btn = self._button("Store in KB", gtk.STOCK_SAVE)
        rvrt_btn = self._button("Revert", gtk.STOCK_REVERT_TO_SAVED)
        close_btn = self._button(_('Close'), stock=gtk.STOCK_CLOSE)
        close_btn.connect("clicked", self._btn_close)

        # Main hbox
        hbox = gtk.HBox()
        hbox.show()
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_LARGE_TOOLBAR)
        image.set_padding(10, 10)
        image.show()
        
        hbox.pack_start(image)

        # Show the description
        # The long description of the plugin
        long_label = gtk.Label()
        text = 'Complete the following parameters to create a new <b>%s</b>'\
               ' vulnerability and add it to the knowledge base. Once added'\
               ' you can use the exploits as usual.'
        long_label.set_markup(text % template.get_vulnerability_name())
        long_label.set_alignment(0.0, 0.5)
        long_label.set_padding(5, 10)
        long_label.set_line_wrap(True)
        long_label.show()
        hbox.pack_start(long_label)
        
        self.vbox.pack_start(hbox)

        # Save it , I need it when I inherit from this class
        template.pname = None
        template.ptype = None
        self._template = template
        self._panel = OnlyOptions(self, w3af, template, save_btn, rvrt_btn)
        self.vbox.pack_start(self._panel)
        
        # Trick/Hack! I connect to the clicked here, after the onlyoptions
        # does the same thing, in order to have the onlyoptions class save
        # the information to the template and then I store it
        save_btn.connect("clicked", self._store_in_kb, template)
        
        self.like_initial = True
        self.connect("event", self._evt_close)
        self.run()
        self.destroy()

    def _store_in_kb(self, widg, template):
        """
        Stores the template information as a Vuln object in the KB.
        """
        if self._panel.saved_successfully:
            template.store_in_kb()
