/* $Id: prefs_context.c,v 1.13 2007-03-05 21:54:03 jan Exp $
 *
 * Copyright (C) 2004 by Intevation GmbH
 * Author(s):
 * Thomas Arendsen Hein <thomas@intevation.de>
 *
 * This program is free software under the GNU GPL (>=v2)
 * Read the file COPYING coming with the software for details.
 *
 * In addition, as a special exception, Intevation GmbH gives
 * 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.
 */

#include <includes.h>
#include "nessus_i18n.h"

#include <gtk/gtk.h>
#include "globals.h"
#include "nessus.h"
#include "context.h"
#include "preferences.h"
#include "error_dlg.h"
#include "comm.h"
#include "prefs_dialog.h"
#include "prefs_target.h"
#include "prefs_dialog_plugins_prefs.h"
#include "prefs_dialog_scan_opt.h"
#include "prefs_plugins.h"
#include "prefs_dialog_user.h"
#include "backend.h"
#include "prefs_context.h"
#include "report.h"
#include "prefs_report.h"
#include "prefs_scope_tree.h"
#include "prefs_comment.h"

#include "../xpm/connect_16.xpm"
#include "../xpm/disconnect_16.xpm"

/**
 * Sets sensitivity of a GTK Widget that is registered as name in the arglist
 * MainDialog.
 * 
 * @param name   Name of the entry for the GTK Widget in the arglist MainDialog.
 * @param enable If true the widget will be sensitive, if false it will be
 *               insensitive (not reacting to user input).
 */
void
prefs_context_enable_widget (const gchar* name, gboolean enabled)
{
  GtkWidget *widget = GTK_WIDGET(arg_get_value(MainDialog, name));

  gtk_widget_set_sensitive(widget, enabled);
}

/**
 * @brief Dis/Enables items in menu and buttons, according to context type and
 * @brief state (action).
 * 
 * E.g. disables menu items in the report menu if currently selected context
 * is not a report.
 * 
 * @param context The context according to which switch sensitivity of widgets.
 */
void
prefs_context_update_widgets (struct context *context)
{
  static context_type last_type = CONTEXT_ENUM;
  static context_action last_action = CONTEXT_IDLE;
  context_type type = context->type;
  context_type action = context->action;
  int do_enable;

  // Set the text of "apply overrides" menu- item according to state of context
  // Handier way of doing this (item_set_label) in Gtk 2.16
  GtkWidget *overrides_menu_item = GTK_WIDGET(arg_get_value(MainDialog, "APPLYOVERRIDESTOREPORT_MENUITEM"));
  GtkWidget *menu_label = gtk_bin_get_child(GTK_BIN(overrides_menu_item));
  if (context->is_severity_mapped == TRUE)
    {
      gtk_label_set_text(GTK_LABEL(menu_label), _("Undo severity override"));
    }
    else
    {
      gtk_label_set_text(GTK_LABEL(menu_label), _("Override severities"));
    }

  if(type != last_type || action != last_action )
  {
    prefs_context_enable_widget("RENAMETASK_MENUITEM", type >= CONTEXT_TASK);
    prefs_context_enable_widget("DELETETASK_MENUITEM", type >= CONTEXT_TASK);

    prefs_context_enable_widget("EXECSCOPE_MENUITEM", type >= CONTEXT_SCOPE && action == CONTEXT_IDLE );
#if 0
    prefs_context_enable_widget("EDITPRIORITIES_MENUITEM", type >= CONTEXT_REPORT);
#endif
    prefs_context_enable_widget("NEWSCOPE_MENUITEM", type >= CONTEXT_TASK);
    prefs_context_enable_widget("RENAMESCOPE_MENUITEM", type >= CONTEXT_SCOPE);
    prefs_context_enable_widget("DELETESCOPE_MENUITEM", type >= CONTEXT_SCOPE);
    prefs_context_enable_widget("OPENSCOPE_MENUITEM", type >= CONTEXT_TASK);
    prefs_context_enable_widget("SAVESCOPE_MENUITEM", type >= CONTEXT_SCOPE);

    prefs_context_enable_widget("RENAMEREPORT_MENUITEM", type >= CONTEXT_REPORT);
    prefs_context_enable_widget("DELETEREPORT_MENUITEM", type >= CONTEXT_REPORT);

    prefs_context_enable_widget("APPLYOVERRIDESTOREPORT_MENUITEM", type >= CONTEXT_REPORT);

    prefs_context_enable_widget("IMPORTREPORT_MENUITEM", type >= CONTEXT_SCOPE);
    prefs_context_enable_widget("EXPORTREPORT_MENUITEM", type >= CONTEXT_REPORT);
    prefs_context_enable_widget("PRINTREPORT_MENUITEM", type >= CONTEXT_REPORT);

    prefs_context_enable_widget("DELETECONTEXT_BUTTON", type >= CONTEXT_TASK);
    prefs_context_enable_widget("EXECSCOPE_BUTTON", type == CONTEXT_SCOPE && action == CONTEXT_IDLE);
  }
  last_type = type;
  last_action = action;

  prefs_context_enable_widget("MOVESCOPE_MENUITEM",
      type >= CONTEXT_SCOPE && Global->children->next);

  do_enable = (type == CONTEXT_SCOPE || type == CONTEXT_GLOBAL)
    && context->socket < 0;
  prefs_context_enable_widget("CONNECT_MENUITEM", do_enable);
  prefs_context_enable_widget("CONNECT_BUTTON", do_enable);

  do_enable = (type == CONTEXT_SCOPE || type == CONTEXT_GLOBAL )
    && context->socket >= 0 
    && action == CONTEXT_IDLE;
  prefs_context_enable_widget("DISCONNECT_MENUITEM", do_enable);
  prefs_context_enable_widget("DISCONNECT_BUTTON", do_enable);
}

void
prefs_context_switch_notebookpage(notebook, page, page_num, user_data)
  GtkNotebook *notebook;
  GtkNotebookPage *page;
  guint page_num;
  gpointer user_data;
{
  GtkWidget *newpage = gtk_notebook_get_nth_page(notebook, page_num);

  if(!GTK_WIDGET_IS_SENSITIVE(newpage))
  {
    show_info(_("This page is not available in the current context."));
    prefs_context_update(Context);
  }
}

void
prefs_context_enable_notebookpage(notebook, pagenr, enable)
  int pagenr;
  GtkWidget *notebook;
  gboolean enable;
{
  GtkWidget *page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), pagenr);
  GtkWidget *label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), page);

  gtk_widget_set_sensitive(label, enable);
  gtk_widget_set_sensitive(page, enable);
}

/**
 * @brief Dis/enable notebook pages ("tabs" on the right side of the gui like
 * Comment, Options, Report).
 */
void
prefs_context_update_notebook (gboolean comment, gboolean options, gboolean report)
{
  GtkWidget *notebook = arg_get_value(MainDialog, "CONTEXTNOTEBOOK");

  prefs_context_enable_notebookpage(notebook, 0, comment);
  prefs_context_enable_notebookpage(notebook, 1, options);
  prefs_context_enable_notebookpage(notebook, 2, report);

  if(report)
    gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 2);
  else if(options)
    gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 1);
  else if(comment)
    gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
}

void
prefs_context_update_comment (struct context *context)
{
  fill_comment(context, arg_get_value(MainDialog, "COMMENT"));
}

void
prefs_context_enable_options_frame(name, enabled)
  const gchar *name;
  gboolean enabled;
{
  struct arglist *options = arg_get_value(MainDialog, "OPTIONS");
  struct arglist *suboptions = arg_get_value(options, name);
  GtkWidget *widget = GTK_WIDGET(arg_get_value(suboptions, "FRAME"));

  gtk_widget_set_sensitive(widget, enabled);
}

void
prefs_context_update_options_enable(gboolean enable)
{
  struct arglist *options = arg_get_value(MainDialog, "OPTIONS");

  prefs_dialog_scan_opt_readonly(arg_get_value(options, "SCAN_OPTIONS"),
      !enable);
  prefs_plugins_read_only(arg_get_value(options, "PLUGINS"), !enable);
  prefs_dialog_plugins_prefs_read_only(arg_get_value(options, "PLUGINS_PREFS"),
      !enable);
  prefs_context_enable_options_frame("TARGET", enable);
  prefs_dialog_user_read_only(arg_get_value(options, "USER"), !enable);
  prefs_context_enable_options_frame("SAVE_KB", enable);
}

void
prefs_context_update_options(context)
  struct context *context;
{
  if(prefs_has_options(context))
  {
    prefs_dialog_set_defaults(context, MainDialog);
    fill_scanner_list(context, arg_get_value(arg_get_value(MainDialog, "SCAN_OPTIONS"), "SCANNERS_LIST"));
    fill_plugins_family(context, arg_get_value(MainDialog, "PLUGINS"));
    fill_rules_list(context, arg_get_value(MainDialog, "USER"));
    prefs_plugins_prefs_redraw(context, arg_get_value(MainDialog, "PLUGINS_PREFS"));
    prefs_context_update_options_enable(context->type < CONTEXT_REPORT);
  }
}

/**
 * Rewires MainDialog->'REPORT' arglist BE and REPORT_CONTEXT entries,
 * then calls scopetreeview_counters_update to update the table in the left part
 * of the GUI and prefs_report_update
 */
void
prefs_context_update_report (struct context *context)
{
  int be;
  struct arglist *ctrls_report;
  char *fname;

  /* Load the corresponding report and update the report page */
  fname = report_get_filename(context);
  ctrls_report = arg_get_value(MainDialog, "REPORT");

  /* But first discard the current one */
  be = GPOINTER_TO_SIZE (arg_get_value(ctrls_report, "BE"));
  if (be >= 0)
    {
      backend_dispose(be);
      arg_set_value(ctrls_report, "BE", sizeof(int), (void *)-1);
      arg_set_value(ctrls_report, "REPORT_CONTEXT", -1, (void *)NULL);
    }
  if (fname && check_is_file(fname))
    {
      be = backend_import_report(fname);
      efree(&fname);
      arg_set_value(ctrls_report, "BE", sizeof(gpointer), GSIZE_TO_POINTER(be));
      arg_set_value(ctrls_report, "REPORT_CONTEXT", -1, (void *)context);
      scopetreeview_counters_update(context, be, fname);
    }
  prefs_report_update(ctrls_report, FALSE);
}

/**
 * @brief Update the connection state image.
 *
 * If is_connected is true, the picture shown in the image is set to
 * connect_16, otherwise it is set to disconnect_16.
 */
static void
update_connection_image(image, is_connected)
  GtkWidget *image;
  int is_connected;
{
  static GdkPixbuf *connected = NULL;
  static GdkPixbuf *disconnected = NULL;

  if (connected == NULL)
  {
    connected = gdk_pixbuf_new_from_xpm_data(connect_16);
  }
  if (disconnected == NULL)
  {
    disconnected = gdk_pixbuf_new_from_xpm_data(disconnect_16);
  }

  gtk_image_set_from_pixbuf(GTK_IMAGE(image),
      is_connected ? connected : disconnected);
}

/**
 * @brief Updates connection icon and string in status bar.
 *
 * The statusbar in the OpenVAS GUI hosts a small icon showing if the currently
 * selected task (or Globals) is connected or not and a string showing 
 * connection details (e.g. "Connection: user@localhost").
 * 
 * @param context The context to draw information from.
 */
void
prefs_context_update_statusbar (struct context *context)
{
  GtkStatusbar * statusbar = GTK_STATUSBAR(arg_get_value(MainDialog,
	"CONNECT_STATUSBAR"));
  gchar * connect_status;
  int port = prefs_get_int(context, "nessusd_port");
  const gchar * username = prefs_get_string(context, "nessusd_user");
  const gchar * hostname = prefs_get_string(context, "nessusd_host");
  GtkWidget *icon = arg_get_value(MainDialog, "CONNECT_IMAGE");

  gtk_statusbar_pop(statusbar,
    gtk_statusbar_get_context_id(statusbar, "connection"));

  if(context->socket >= 0)
  {
    if (port == GPOINTER_TO_SIZE(prefs_get_default(context, "nessusd_port")))
      connect_status = g_strdup_printf(_("Connection: %s@%s"),
	username, hostname);
    else if (port >= 0)
      connect_status = g_strdup_printf(_("Connection: %s@%s:%d"),
	username, hostname, port);
    else
      connect_status = g_strdup_printf(_("Connection: user %s"),
	username);
    update_connection_image(icon, 1);
  }
  else
  {
    connect_status = g_strdup_printf(_("not connected"));
    update_connection_image(icon, 0);
  }

  gtk_statusbar_push(statusbar,
      gtk_statusbar_get_context_id(statusbar, "connection"), connect_status);
  g_free(connect_status);
}

void
prefs_context_update (struct context *context)
{
  GtkWidget *scopetree = GTK_WIDGET(
      arg_get_value(arg_get_value(MainDialog, "SCOPETREE"), "TREEVIEW"));
  GtkTreePath *path = gtk_tree_row_reference_get_path(context->treerowref);
  GtkTreePath *curpath;
  GtkWidget *contextframe = arg_get_value(MainDialog, "CONTEXTFRAME");
  gchar *text = NULL;
  GtkTreeViewColumn *column;

  /* Make sure to synchronise other data structures with the GUI elements */
  prefs_dialog_apply(Context, MainDialog);
  prefs_dialog_apply_plugin_prefs(Context->plugins);
  prefs_dialog_apply_plugin_prefs(Context->scanners);
  context_sync_plugin_prefs(Context);

  scope_move_menuitem_enable(Context, TRUE);
  scope_move_menuitem_enable(context, FALSE);

  Context = context;	/* change current context */

  column = gtk_tree_view_get_column(GTK_TREE_VIEW(scopetree), 0);
  gtk_tree_view_get_cursor(GTK_TREE_VIEW(scopetree), &curpath, NULL);
  if(!curpath || !path || gtk_tree_path_compare(curpath, path))
    gtk_tree_view_set_cursor(GTK_TREE_VIEW(scopetree), path, column, FALSE);

  if(curpath)
    gtk_tree_path_free(curpath);
  if(path)
    gtk_tree_path_free(path);

  switch(context->type)
  {
    case CONTEXT_GLOBAL:
      prefs_context_update_notebook(FALSE, TRUE, FALSE);
      text = g_strdup (prefs_get_string(context, "name"));
      break;
    case CONTEXT_TASK:
      prefs_context_update_notebook(TRUE, FALSE, FALSE);
      text = g_strdup_printf(_("Task: %s"),
	  prefs_get_string(context, "name"));
      break;
    case CONTEXT_SCOPE:
      /* Disable options and comments while scanning */
      prefs_context_update_notebook(context->action != CONTEXT_SCANNING,
	  context->action != CONTEXT_SCANNING, FALSE);
      text = g_strdup_printf(_("Scope: %s (Task: %s)"),
	  prefs_get_string(context, "name"),
	  prefs_get_string(context->parent, "name"));
      break;
    case CONTEXT_REPORT:
      prefs_context_update_notebook(TRUE, prefs_has_options(context), TRUE);
      text = g_strdup_printf (_("Report for scope: %s (Task: %s)"),
                              prefs_get_string(context->parent, "name"),
                              prefs_get_string(context->parent->parent, "name"));
      break;
    default:
      show_error(_("prefs_context_update called with illegal context."));
  }
  gtk_frame_set_label(GTK_FRAME(contextframe), text);
  g_free(text);

  /* make sure the plugin cache of reports or other contexts is
   * loaded */
  context_load_plugin_cache(context);

  prefs_context_update_widgets(context);
  prefs_context_update_comment(context);
  prefs_context_update_options(context);
  prefs_context_update_report(context);
  prefs_context_update_statusbar(context);
}
