/* OpenVAS-Client
 * $Id$
 * Description: A form for severity_overrides.
 *
 * Author(s):
 * Felix Wolfsteller <felix.wolfsteller@intevation.de>
 *
 * Copyright:
 * Copyright (C) 2009 Intevation GmbH
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * or, at your option, any later version as published by the Free
 * Software Foundation
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * In addition, as a special exception, you have
 * permission to link the code of this program with the OpenSSL
 * library (or with modified versions of OpenSSL that use the same
 * license as OpenSSL), and distribute linked combinations including
 * the two. You must obey the GNU General Public License in all
 * respects for all of the code used other than OpenSSL. If you
 * modify this file, you may extend this exception to your version
 * of the file, but you are not obligated to do so. If you do not
 * wish to do so, delete this exception statement from your version.
 */

/**
 * @file
 * 
 * (severity_override)Forms can let the user inspect, add and (not yet) edit
 * overrides.
 * 
 */

#ifdef USE_GTK

#include "error_dlg.h"
#include "nessus_i18n.h"
#include "nvt_pref_sshlogin.h"
#include "parser.h"
/* for prefs_get_nessushome */
#include "preferences.h"
#include "severity_override_form.h"
#include "system.h"

extern severity_filter_t * global_filter;

static gboolean severity_override_form_valid (severity_override_form_t* form,
                              severity_filter_t* filter,
                              gboolean show_warnings);

/**
 * @brief As a callback for an "Add"- Button, adds a severity_override that
 * @brief is created from a form to the global_filter. Saves the filter
 * @brief afterwards.
 */
static void
add_override_to_global_filter (GtkWidget* parent,
                               severity_override_form_t* form)
{
  // Add override if possible
  if (global_filter != NULL && form != NULL )
    {
      if ( severity_override_form_valid (form, NULL, TRUE) == FALSE)
        {
          return;
        }
      // Will not return an override if it would be conflicting
      const severity_override_t* override = severity_override_form_to_override (form);
      if (override != NULL)
        {
          if (severity_filter_add (global_filter, override) == TRUE)
            show_info (_("Severity Override %s has been added to the global filter."), override->name);
          else
            show_error (_("An error occurred when adding the Override %s to the global filter."), override->name);
        }
        else
          show_error (_("A conflicting Severity Override already exists in the list of overrides."));
    }
}

/**
 * @brief Creates and layouts a new override_form.
 * 
 * @param action If action == SOF_ADD, add an add-button that on click adds the
 *               override to the global filter. If action == SOF_VIEW, lock the
 *               form.
 * 
 * @return Fresh severity_override_form_t.
 */
static severity_override_form_t*
severity_override_form_new (formaction action)
{
  gchar* map_from_string;
  severity_override_form_t* form = emalloc (sizeof (severity_override_form_t));
  gint rowidx = 0;
  
  form->action = action;
  GtkWidget* add_button = NULL;
  
  // Layouting
  form->vbox  = gtk_vbox_new (FALSE, 5);
  form->table = gtk_table_new (5, 2, TRUE);
  
  // Textual Input
  form->name_label   = GTK_LABEL(gtk_label_new (_("Name: ")));
  form->name_entry   = GTK_ENTRY(gtk_entry_new ());
  form->reason_label = GTK_LABEL(gtk_label_new (_("Reason: ")));
  form->reason_entry = GTK_ENTRY(gtk_entry_new ());

  // Info
  form->port_label       = gtk_label_new (_("Port"));
  form->port_value_label = gtk_label_new ("");
  form->host_label       = gtk_label_new (_("Host"));
  form->host_value_label = gtk_label_new ("");
  form->oid_label        = gtk_label_new (_("OID of NVT"));
  form->oid_value_label  = gtk_label_new ("");

  // Mapping
  map_from_string = g_strdup_printf (_("Map from %s to"), "?");
  form->map_from_label = GTK_LABEL(gtk_label_new (map_from_string));
  g_free (map_from_string);
  form->severity_to_combobox = GTK_COMBO_BOX(gtk_combo_box_new_text ());
  gtk_combo_box_append_text (form->severity_to_combobox, priority_type_to_str (MSG_HOLE));
  gtk_combo_box_append_text (form->severity_to_combobox, priority_type_to_str (MSG_INFO));
  gtk_combo_box_append_text (form->severity_to_combobox, priority_type_to_str (MSG_NOTE));
  gtk_combo_box_append_text (form->severity_to_combobox, priority_type_to_str (MSG_FALSE));

  // Actions
  if (action == SOF_ADD)
    {
      add_button = gtk_button_new_with_label (_("Add Override to filter"));
      g_signal_connect (GTK_OBJECT(add_button), "clicked", GTK_SIGNAL_FUNC(add_override_to_global_filter),
                                form);
    }

  // Make labels left-aligned
  gtk_misc_set_alignment (GTK_MISC(form->name_label), 0.0, 0.5);
  gtk_misc_set_alignment (GTK_MISC(form->reason_label), 0.0, 0.5);
  gtk_misc_set_alignment (GTK_MISC(form->host_label), 0.0, 0.5);
  gtk_misc_set_alignment (GTK_MISC(form->port_label), 0.0, 0.5);
  gtk_misc_set_alignment (GTK_MISC(form->oid_label), 0.0, 0.5);
  gtk_misc_set_alignment (GTK_MISC(form->map_from_label), 0.0, 0.5);
  gtk_misc_set_alignment (GTK_MISC(form->host_value_label), 0.0, 0.5);
  gtk_misc_set_alignment (GTK_MISC(form->port_value_label), 0.0, 0.5);
  gtk_misc_set_alignment (GTK_MISC(form->oid_value_label), 0.0, 0.5);

  // Layouting, fill dialog
  gtk_table_attach (GTK_TABLE (form->table), GTK_WIDGET (form->name_label), 0, 1, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  gtk_table_attach (GTK_TABLE (form->table), GTK_WIDGET (form->name_entry), 1, 2, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  ++rowidx;
  gtk_table_attach (GTK_TABLE (form->table), GTK_WIDGET(form->reason_label), 0, 1, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  gtk_table_attach (GTK_TABLE (form->table), GTK_WIDGET(form->reason_entry), 1, 2, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  ++rowidx;
  gtk_table_attach (GTK_TABLE (form->table), form->host_label, 0, 1, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  gtk_table_attach (GTK_TABLE (form->table), form->host_value_label, 1, 2, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  ++rowidx;
  gtk_table_attach (GTK_TABLE (form->table), form->port_label, 0, 1, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  gtk_table_attach (GTK_TABLE (form->table), form->port_value_label, 1, 2, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  ++rowidx;
  gtk_table_attach (GTK_TABLE (form->table), form->oid_label, 0, 1, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  gtk_table_attach (GTK_TABLE (form->table), form->oid_value_label, 1, 2, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  ++rowidx;
  gtk_table_attach (GTK_TABLE (form->table), GTK_WIDGET(form->map_from_label), 0, 1, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  gtk_table_attach (GTK_TABLE (form->table), GTK_WIDGET(form->severity_to_combobox), 1, 2, rowidx, rowidx+1, GTK_FILL, GTK_SHRINK, 5, 0);
  ++rowidx;
  gtk_box_pack_start (GTK_BOX(form->vbox), form->table, FALSE, FALSE, 0);
  if (action == SOF_ADD)
    gtk_box_pack_start (GTK_BOX(form->vbox), add_button, FALSE, FALSE, 0);
  else if (action == SOF_VIEW)
    severity_override_form_lock (form);

  return form;
}

/**
 * @brief Destroys widgets contained in the form and frees associated memory.
 * 
 * @param form The form to free.
 */
void
severity_override_form_free (severity_override_form_t* form)
{
  if (form == NULL)
    return;

  if (form->name_label)
    gtk_widget_destroy (GTK_WIDGET(form->name_label));
  if (form->name_entry)
    gtk_widget_destroy (GTK_WIDGET(form->name_entry));
  if (form->reason_label)
    gtk_widget_destroy (GTK_WIDGET(form->reason_label));
  if (form->reason_entry)
    gtk_widget_destroy (GTK_WIDGET(form->reason_entry));
  if (form->host_label)
    gtk_widget_destroy (form->host_label);
  if (form->host_value_label)
    gtk_widget_destroy (form->host_value_label);
  if (form->port_label)
    gtk_widget_destroy (form->port_label);
  if (form->port_value_label)
    gtk_widget_destroy (form->port_value_label);
  if (form->oid_label)
    gtk_widget_destroy (form->oid_label);
  if (form->oid_value_label)
    gtk_widget_destroy (form->oid_value_label);
  if (form->map_from_label)
    gtk_widget_destroy (GTK_WIDGET(form->map_from_label));
  if (form->severity_to_combobox)
    gtk_widget_destroy (GTK_WIDGET(form->severity_to_combobox));
  if (form->table)
    gtk_widget_destroy (form->table);
  if (form->vbox)
    gtk_widget_destroy (form->vbox);
  g_free (form->severity_from_string);

  efree (&form);
}

/**
 * @brief Make widgets insensitive, so that no further editing is possible.
 * 
 * @param form Form to lock.
 */
void
severity_override_form_lock (severity_override_form_t* form)
{
  gtk_widget_set_sensitive (GTK_WIDGET(form->name_entry), FALSE);
  gtk_widget_set_sensitive (GTK_WIDGET(form->reason_entry), FALSE);
  gtk_widget_set_sensitive (GTK_WIDGET(form->severity_to_combobox), FALSE);
  // Depending on action, buttons might have to be added / removed
}

/**
 * @brief Creates a fresh override form and fills in values from an override.
 * 
 * @param action The action to perform with this form. Does influence the look &
 *               feel and which buttons are available (SOF_ADD -> add-button).
 * 
 * @return Fresh override_form with values according to the override.
 */
severity_override_form_t*
severity_override_form_from_override (const severity_override_t* override,
                                      formaction action)
{
  if (override == NULL) return NULL;

  severity_override_form_t* form = severity_override_form_new (action);
  gtk_entry_set_text (form->name_entry, override->name);
  gtk_entry_set_text (form->reason_entry, override->reason);
  form->severity_from_string = g_strdup (override->severity_from);
  gtk_label_set_text (GTK_LABEL(form->oid_value_label), override->OID);
  gtk_label_set_text (GTK_LABEL(form->host_value_label), override->host);
  gtk_label_set_text (GTK_LABEL(form->port_value_label), override->port);
  
  gchar* map_from_string = g_strdup_printf (_("Map from %s to"), override->severity_from);
  gtk_label_set_text (form->map_from_label, map_from_string);
  g_free (map_from_string);
  
  if (override->severity_to && strcmp(override->severity_from, ""))
    // Default: severity_from -> severity_to (no change)
    text_combobox_set_active_text (GTK_WIDGET(form->severity_to_combobox), override->severity_from);
  
  return form;
}


/**
 * @brief Returns TRUE if a severity_override_form shows sane data.
 * 
 * The checks are not done thoroughly on all fields, but the most important
 * ones.
 * 
 * @param form          The form to validate
 * @param filter        The filter to associate the override to (yet ignored).
 * @param show_warnings If TRUE and return value is FALSE, warnings will be
 *                      shown with instructions how to make the form valid.
 * 
 * @return FALSE if no form was passed or no form does not show sane data, TRUE
 *         otherwise.
 */
static gboolean
severity_override_form_valid (severity_override_form_t* form,
                              severity_filter_t* filter,
                              gboolean show_warnings)
{
  if (!form)
    return FALSE;

  if (!strcmp (gtk_entry_get_text (form->name_entry), ""))
    {
      if (show_warnings) show_warning (_("Please provide a better name."));
      return FALSE;
    }

  if (!strcmp (gtk_entry_get_text (form->reason_entry), ""))
    {
      if (show_warnings) show_warning (_("Please provide a better reason."));
      return FALSE;
    }

  gchar* from = form->severity_from_string;
  gchar* to   = gtk_combo_box_get_active_text (form->severity_to_combobox);
  if (!strcmp (from, to))
    {
      if (show_warnings) show_warning (_("Mapping %s to %s would create an effectless override.\nPlease choose a different severity."), from, to);
      return FALSE;
    }

  if (!strcmp (gtk_label_get_text (GTK_LABEL(form->oid_value_label)), ""))
    {
      if (show_warnings) show_warning (_("No information about OID of NVT found!"));
      return FALSE;
    }

  // Now one could check if a similar override alread exists in the provided filter
  return TRUE;
}

/**
 * @brief Creates a fresh severity_override from the values within a
 * @brief override_form.
 * 
 * Does not validate the input.
 * 
 * @return Override as specified in the form.
 */
const severity_override_t*
severity_override_form_to_override (const severity_override_form_t* form)
{
  // Check if it would conflict
  if (severity_filter_contains_conflicting (global_filter,
                          gtk_label_get_text (GTK_LABEL(form->host_value_label)),
                          gtk_label_get_text (GTK_LABEL(form->port_value_label)),
                          gtk_label_get_text (GTK_LABEL(form->oid_value_label)),
                          form->severity_from_string) == TRUE)
    return NULL;
  return severity_override_new (gtk_entry_get_text (form->name_entry),
                                gtk_label_get_text (GTK_LABEL(form->host_value_label)),
                                gtk_label_get_text (GTK_LABEL(form->port_value_label)),
                                gtk_label_get_text (GTK_LABEL(form->oid_value_label)),
                                gtk_entry_get_text (form->reason_entry),
                                form->severity_from_string,
                                gtk_combo_box_get_active_text (form->severity_to_combobox),
                                TRUE);
}

#endif /* USE_GTK */
