"""
exploits.py

Copyright 2007 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.ui.gui import helpers
from w3af.core.ui.gui.pluginEditor import pluginEditor
from w3af.core.ui.gui.tabs.exploit.utils import get_exploitable_vulns
from w3af.core.ui.gui.tabs.exploit.exploit_all import effectively_exploit_all


class ExploitTree(gtk.TreeView):
    """A list showing all the plugins of "attack" type.

    :param w3af: The main core class.

    :author: Facundo Batista <facundobatista =at= taniquetil.com.ar>
    """
    def __init__(self, w3af, config_panel):
        self.w3af = w3af
        self.config_panel = config_panel

        # create the ListStore, with the plugin name twice (the first could
        # go bold, the second is the original name always)
        self.liststore = gtk.ListStore(str, str)

        # just build the tree with the plugin names
        for plugin in sorted(w3af.plugins.get_plugin_list("attack")):
            self.liststore.append([plugin, plugin])

        # we will not ask for the plugin instances until needed, we'll
        # keep them here:
        self.plugin_instances = {}

        # create the TreeView using liststore
        super(ExploitTree, self).__init__(self.liststore)

        # signals
        self.connect('button-release-event', self.popup_menu)
        self.connect('cursor-changed', self._changed_selection)

        # create a TreeViewColumn for the text
        tvcolumn = gtk.TreeViewColumn(_('Exploits'))
        cell = gtk.CellRendererText()
        tvcolumn.pack_start(cell, True)
        tvcolumn.add_attribute(cell, 'markup', 0)
        self.append_column(tvcolumn)

        # drag and drop setup, this is the SOURCE
        target = [("explot-activ", 0, 1)]
        self.enable_model_drag_source(
            gtk.gdk.BUTTON1_MASK, target, gtk.gdk.ACTION_COPY)

        #self.set_enable_tree_lines(True)
        self.show()

    def set_filter(self, vuln):
        new_liststore = gtk.ListStore(str, str)
        for pname in sorted(self.w3af.plugins.get_plugin_list("attack")):
            exploit = self.w3af.plugins.get_plugin_inst("attack", pname)
            thisvulns = get_exploitable_vulns(exploit)
            markedname = ("<b>%s</b>" % pname) if vuln in thisvulns else pname
            new_liststore.append([markedname, pname])
        self.set_model(new_liststore)
        self.liststore = new_liststore

    def _changed_selection(self, *w):
        """Changed which exploit is selected."""
        exploit = self.get_selected_exploit()
        self.vulnerabs.set_filter(exploit)
        
        longdesc = exploit.get_long_desc()
        longdesc = helpers.clean_description(longdesc)
        self.config_panel.config(self, exploit, longdesc)

        # un-bold the rest
        for row in self.liststore:
            if row[1] != exploit.pname:
                row[0] = row[1]

    def config_changed(self, initial):
        """
        Called when a configuration parameter in an exploit plugin changes.
        """
        pass

    def get_selected_exploit(self):
        """Returns the selected exploit.

        :return: The selected exploit.
        """
        (path, column) = self.get_cursor()
        if path is None:
            return None

        # Get the information about the click
        plugin = self.get_plugin_inst(path)
        return plugin

    def popup_menu(self, tv, event):
        """Shows a menu when you right click on a plugin.

        :param tv: the treeview.
        :param event: The GTK event
        """
        if event.button != 3:
            return

        (path, column) = tv.get_cursor()
        # Is it over a plugin name ?
        if path is not None and len(path) == 1:
            # Get the information about the click
            plugin = self.get_plugin_inst(path)
            pname = self.liststore[path][1]

            # Ok, now I show the popup menu !
            # Create the popup menu
            gm = gtk.Menu()

            # And the items
            e = gtk.MenuItem(_("Edit plugin..."))
            e.connect('activate', self._handleEditPluginEvent, pname, path)
            gm.append(e)
            e = gtk.MenuItem(_("Exploit ALL vulns"))
            e.connect('activate', self._exploit_all, pname, False)
            gm.append(e)
            e = gtk.MenuItem(_("Exploit all until first successful"))
            e.connect('activate', self._exploit_all, pname, True)
            gm.append(e)

            gm.show_all()
            gm.popup(None, None, None, event.button, event.time)

    def _handleEditPluginEvent(self, widget, plugin_name, path):
        """
        I get here when the user right clicks on a plugin name, then he clicks on "Edit..."
        This method calls the plugin editor with the corresponding parameters.
        """
        def f(t, n):
            self._finishedEditingPlugin(path, plugin_name)
        pluginEditor("attack", plugin_name, f)

    def _finishedEditingPlugin(self, path, plugin_name):
        """
        This is a callback that is called when the plugin editor finishes.
        """
        del self.plugin_instances[path]
        self.w3af.plugins.reload_modified_plugin('attack', plugin_name)

    def _exploit_all(self, widget, pname, stoponfirst):
        """Exploit all the vulns."""
        effectively_exploit_all(self.w3af, [pname], stoponfirst)

    def get_plugin_inst(self, path):
        """Caches the plugin instance.

        :param path: where the user is in the plugin list
        :return The plugin
        """
        try:
            return self.plugin_instances[path]
        except KeyError:
            pass

        # path can be a tuple of one or two values here
        pname = self.liststore[path][1]
        plugin = self.w3af.plugins.get_plugin_inst("attack", pname)
        plugin.pname = pname
        plugin.ptype = "attack"
        self.plugin_instances[path] = plugin
        return plugin


