/* Nessus
 * Copyright (C) 1999, 2000 Renaud Deraison
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * In addition, as a special exception, Renaud Deraison
 * gives permission to link the code of this program with any
 * version of the OpenSSL library which is distributed under a
 * license identical to that listed in the included COPYING.OpenSSL
 * file, 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>

#ifdef USE_GTK
#include <gtk/gtk.h>
#include "nessus_plugin.h"
#include "context.h"
#include "comm.h"
#include "prefs_dialog_plugins_prefs.h"
#include "listnotebook.h"
#include "readonly.h"
#include "error_dlg.h"
#include "nvt_pref_sshlogin.h"

#include "nessus_i18n.h"

static GtkWidget * pprefs_add_notebook_page(struct arglist *, char *, int);
static void pprefs_add_entry(struct arglist *, struct arglist *, char *, GtkWidget*);
static void pprefs_add_password(struct arglist *, struct arglist *, char *, GtkWidget*);
static void pprefs_add_file(struct arglist *, struct arglist *, char *, GtkWidget*);
static void pprefs_add_checkbox(struct arglist *, struct arglist *, char *, GtkWidget*);
static void pprefs_add_radio(struct arglist *, struct arglist *, char *, GtkWidget*);

/**
 * Adds and hooks up (or resets) the two notebook pages for Plugin Preferences 
 * and Credentials.
 * These pages are itself notebooks with dynamical content.
 * 
 * @param ctrls Arglist to hook the notebooks into.
 */
static void
create_plugin_prefs_containers(struct arglist* ctrls)
{
  GtkWidget *listnotebook, *cred_listnotebook;

  listnotebook = listnotebook_new(FALSE, TRUE);
  read_only_set_recurse(listnotebook);
  gtk_container_set_border_width(GTK_CONTAINER(listnotebook), 10);

  cred_listnotebook = listnotebook_new(FALSE, TRUE);
  read_only_set_recurse(cred_listnotebook);
  gtk_container_set_border_width(GTK_CONTAINER(cred_listnotebook), 10);
  
  /* On the first call we have to add the values, on subsequent calls we
   * just set them */
  if (arg_get_value(ctrls, "PLUGIN_PREFS") != NULL)
  {
    arg_set_value(ctrls, "PLUGIN_PREFS", -1, listnotebook);
    arg_set_value(ctrls, "PLUGIN_CREDENTIALS", -1, cred_listnotebook);
  }
  else
  {
    arg_add_value(ctrls, "PLUGIN_PREFS", ARG_PTR, -1, listnotebook);
    arg_add_value(ctrls, "PLUGIN_CREDENTIALS", ARG_PTR, -1, cred_listnotebook);
  }
}

/**
 * Creates "Advanced Plugins preferences" and  "Credentials" frames.
 * Calls create_plugin_prefs_containers to init content in them.
 * 
 * @return Arglist with Plugins preferences and Credentials hooked into.
 */
struct arglist *
prefs_dialog_plugins_prefs()
{
  struct arglist *ctrls = emalloc(sizeof(struct arglist));
  GtkWidget *frame, * cred_frame;
  GtkWidget *plugin_prefs, *plugin_credentials;

  frame = gtk_frame_new(_("Advanced Plugins preferences"));
  gtk_container_border_width(GTK_CONTAINER(frame), 10);
  gtk_widget_show(frame);
  arg_add_value(ctrls, "FRAME", ARG_PTR, -1, frame);
  read_only_set_recurse(frame);

  cred_frame = gtk_frame_new(_("Credentials"));
  gtk_container_border_width(GTK_CONTAINER(cred_frame), 10);
  gtk_widget_show(cred_frame);
  arg_add_value(ctrls, "FRAME_CREDENTIALS", ARG_PTR, -1, cred_frame);
  read_only_set_recurse(cred_frame);

  create_plugin_prefs_containers(ctrls);

  plugin_prefs = arg_get_value(ctrls, "PLUGIN_PREFS");
  gtk_container_add(GTK_CONTAINER(frame), plugin_prefs);
  gtk_widget_show(plugin_prefs);
  plugin_credentials = arg_get_value(ctrls, "PLUGIN_CREDENTIALS");
  gtk_container_add(GTK_CONTAINER(cred_frame), plugin_credentials);
  gtk_widget_show(plugin_credentials);

  return (ctrls);
}

/**
 * @brief Sets the preference frame and the credentials frame readonly.
 * 
 * @param ctrls Arglist where the FRAME and FRAME_CREDENTIALS are hooked in.
 * @param readonly If true, frames are set readonly.
 */
void
prefs_dialog_plugins_prefs_read_only( struct arglist *ctrls, gboolean readonly)
{
  read_only_set_read_only (GTK_WIDGET(arg_get_value (ctrls, "FRAME")), readonly);
  read_only_set_read_only (GTK_WIDGET(arg_get_value(ctrls, "FRAME_CREDENTIALS")),
                           readonly);
}

/**
 * @brief Fill the credentials and plugin preferences pages with the corresponding 
 *        widgets and values.
 * 
 * Widgets are added dynamically, based on the plugin preference type, e.g.
 * for a PREF_CHECKBOX pprefs_add_checkbox() is called.
 * 
 * @param context The context to use.
 * @param ctrls Arglist holding the notebook page widgets.
 * @param plugins Plugin list.
 */
void
prefs_dialog_plugins_prefs_fill (struct context* context, struct arglist* ctrls,
                                 struct nessus_plugin* plugins)
{
  struct arglist *pprefs = arg_get_value(context->prefs, "PLUGINS_PREFS");
  struct nessus_plugin *plugs = plugins;
  GtkWidget *notebook_vbox = NULL;

  while(plugs != NULL )
  {
    struct arglist *prefs;

    if((prefs = plugs->plugin_prefs) != NULL )
    {
      if (strcmp(plugs->family, "Credentials"))
      {
	notebook_vbox = pprefs_add_notebook_page(ctrls, plugs->name, 0);
      }
      else
      {
        notebook_vbox = pprefs_add_notebook_page(ctrls, plugs->name, 1);
      }
      // For each preference of this plugin, add the widget.
      while(prefs && prefs->next)
      {
	char *type, *value;

	type = arg_get_value(prefs->value, "type");
	value = arg_get_value(prefs->value, "value");
	if(type)
	{
	  if(!strcmp(type, PREF_ENTRY))
	    pprefs_add_entry(pprefs, prefs, value, notebook_vbox);
	  else if(!strcmp(type, PREF_PASSWORD))
	    pprefs_add_password(pprefs, prefs, value, notebook_vbox);
	  else if(!strcmp(type, PREF_RADIO))
	    pprefs_add_radio(pprefs, prefs, value, notebook_vbox);
	  else if(!strcmp(type, PREF_CHECKBOX))
	    pprefs_add_checkbox(pprefs, prefs, value, notebook_vbox);
	  else if(!strcmp(type, PREF_FILE))
	    pprefs_add_file(pprefs, prefs, value, notebook_vbox);
          else if(!strcmp(type, PREF_SSH_CREDENTIALS))
            nvt_pref_sshlogin_add(notebook_vbox, prefs);
          else
            show_warning(_("%s asked for unknown preference type %s."), 
                         plugs->name, type);
	}
	prefs = prefs->next;
      }
    }
    plugs = plugs->next;
  }
}

/**
 * Clean up the plugin preferences and plugin
 * preferences widgets
 */
void
prefs_plugins_reset (struct arglist* ctrls, struct nessus_plugin* plugins)
{
  struct arglist *prefs;

  while(plugins != NULL )
  {
    prefs =  plugins->plugin_prefs;
    if(prefs != NULL )
      while(prefs && prefs->next)
      {
	struct arglist *v;

	/*
	 * We keep the first three fields and destroy the others
	 */
	v = prefs->value;	/* value */
	v = v->next;		/* type */
	v = v->next;		/* fullname */
	arg_free(v->next);
	v->next = emalloc(sizeof(struct arglist));
	prefs = prefs->next;
      }
    plugins = plugins->next;
  }
}

/**
 * Redraw the plugins preferences
 */
void
prefs_plugins_prefs_redraw (struct context* context, struct arglist* ctrls)
{
  GtkWidget *frame, *cred_frame;
  GtkWidget *plugin_prefs, *plugin_credentials;

  frame = arg_get_value(ctrls, "FRAME");
  plugin_prefs = arg_get_value(ctrls, "PLUGIN_PREFS");
  cred_frame = arg_get_value(ctrls, "FRAME_CREDENTIALS");
  plugin_credentials = arg_get_value(ctrls, "PLUGIN_CREDENTIALS");

  gtk_widget_hide(plugin_prefs);
  gtk_widget_hide(plugin_credentials);
  gtk_container_remove(GTK_CONTAINER(frame), plugin_prefs);
  gtk_container_remove(GTK_CONTAINER(cred_frame), plugin_credentials);

  plugin_prefs = context->plugin_prefs_widget;
  plugin_credentials = context->plugin_prefs_cred_widget;
  if(plugin_prefs != NULL && plugin_credentials != NULL)
  {
    arg_set_value(ctrls, "PLUGIN_PREFS", -1, plugin_prefs);
    arg_set_value(ctrls, "PLUGIN_CREDENTIALS", -1, plugin_credentials);
  }
  else
  {
    create_plugin_prefs_containers(ctrls);
    plugin_prefs = arg_get_value(ctrls, "PLUGIN_PREFS");
    plugin_credentials = arg_get_value(ctrls, "PLUGIN_CREDENTIALS");

    if((context->plugins && context->plugins->next)
	|| (context->scanners && context->scanners->next))
    {
      g_object_ref(plugin_prefs);
      g_object_ref(plugin_credentials);
      context->plugin_prefs_widget = plugin_prefs;
      context->plugin_prefs_cred_widget = plugin_credentials;

      prefs_plugins_reset(ctrls, context->scanners);
      prefs_plugins_reset(ctrls, context->plugins);
      prefs_dialog_plugins_prefs_fill(context, ctrls, context->scanners);
      prefs_dialog_plugins_prefs_fill(context, ctrls, context->plugins);
    }
  }
  gtk_container_add(GTK_CONTAINER(frame), plugin_prefs);
  /* It can happen that the GTK_PARENT_SENSITIVE flag is not set for the
   * children of plugin_prefs even though all parents are sensitive.
   * The effect of that is that the plugin prefs are insensitive even
   * though they shouldn't be.  Changing the sensitivity of plugin_prefs
   * twice fixes that.
   * It's not clear exactly why GTK_PARENT_SENSITIVE might be unset bit
   * is probably has to do with the gtk_container_add and
   * gtk_container_remove that done in this function and the fact that
   * the sensitivity of the parent widgets of plugin_prefs changes over
   * time.  This might be a GTK bug */
  gtk_widget_set_sensitive(plugin_prefs, FALSE);
  gtk_widget_set_sensitive(plugin_prefs, TRUE);
  gtk_widget_show(plugin_prefs);

  gtk_container_add(GTK_CONTAINER(cred_frame), plugin_credentials);
  /* cred_frame's children may also have an unset GTK_PARENT_SENSITIVE
   * flag so work around it in the same manner as above for plugin_prefs */
  gtk_widget_set_sensitive(cred_frame, FALSE);
  gtk_widget_set_sensitive(cred_frame, TRUE);
  gtk_widget_show(plugin_credentials);
}


/**
 * Adds a notebook page to either the plugin preference notebook or the 
 * credentials notebook and returns the vbox for it.
 * 
 * @param ctrls Arglist in which the other Gtk widgets are held.
 * @param name Name for the new notebook page.
 * @param credentials If 0 page is added to preference notebook, otherwise to 
 *                    credentials notebook.
 * 
 * @return The Gtk vbox nested in a Gtk scrolled window of the new page.
 */
static GtkWidget *
pprefs_add_notebook_page(struct arglist* ctrls, char* name, int credentials)
{
  GtkWidget *vbox = NULL;
  GtkWidget *notebook = NULL;
  
  if (credentials == 0)
    notebook = arg_get_value(ctrls, "PLUGIN_PREFS");
  else
    notebook = arg_get_value(ctrls, "PLUGIN_CREDENTIALS");
  
  GtkWidget *s_window = gtk_scrolled_window_new(NULL, NULL);
  read_only_set_recurse(s_window);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(s_window),
                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_widget_show(s_window);
  vbox = gtk_vbox_new(FALSE, FALSE);
  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(s_window), vbox);
  gtk_widget_show(vbox);

  listnotebook_add_page(notebook, s_window, name, NULL);

  return vbox;
}

/**
 * @brief Displays a file chooser dialog.
 * 
 * The path to the selected file is set as text to the NVT file preferences
 * text entry widget.
 * 
 * @see file_selected
 * 
 * @param b Ignored.
 * @param ctrl Arglist on which to operate (just need the "ENTRY" key/value).
 * 
 * @return Always 0.
 */
static int
select_file (GtkWidget * b, struct arglist *ctrls)
{
  GtkWidget* filechooser;
  GtkWidget* filename_entry;
  // Gtk >= 2.6 defines gtk_file_chooser_set_show_hidden, which might be turned on.
  filechooser = gtk_file_chooser_dialog_new (_("Select file"),
                                            NULL,
                                            GTK_FILE_CHOOSER_ACTION_OPEN,
                                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                                            NULL);

  if (gtk_dialog_run (GTK_DIALOG (filechooser)) == GTK_RESPONSE_ACCEPT)
    {
      char *filename;
      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser));
      filename_entry = arg_get_value (ctrls, "ENTRY");
      gtk_entry_set_text (GTK_ENTRY(filename_entry), filename);
    }
  gtk_widget_destroy (filechooser);

  return 0;
}

/**
 * @brief Returns "yes" or "no" if preference is of type ARG_INT.
 * 
 * @return ("yes" or "no") when pref of type int, value otherwise.
 */
static char*
get_pref_value (struct arglist* prefname, struct arglist* prefvalue,
                char* value)
{
 char* fullname = arg_get_value(prefname->value, "fullname");
 
 if(prefvalue)
 {
   int type;

   if((type = arg_get_type(prefvalue, fullname)) >= 0)
   {
     value = arg_get_value(prefvalue, fullname);
     if(type == ARG_INT)
     {
       if(value)
         value = strdup("yes");
       else
         value = strdup("no");
     }
   }
 }
 
 return value;
}

/**
 * Add "entry" widget corresponding to the "entry" plugin preference.
 */
static void
pprefs_add_entry (struct arglist* pprefs, struct arglist* pref, char* value,
                  GtkWidget* vbox)
{
  GtkWidget *entry, *text, *box;
  char *name = pref->name;
  
  value = get_pref_value(pref, pprefs, value);
  
  box = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
  gtk_widget_show(box);

  text = gtk_label_new(name);
  gtk_box_pack_start(GTK_BOX(box), text, TRUE, TRUE, 5);
  gtk_widget_show(text);

  entry = gtk_entry_new();
  gtk_entry_set_text(GTK_ENTRY(entry), value);
  gtk_box_pack_end(GTK_BOX(box), entry, TRUE, TRUE, 5);
  gtk_widget_show(entry);
  arg_add_value(pref->value, "ENTRY", ARG_PTR, -1, entry);
}


/**
 * Add "password" widget corresponding to the "password" plugin preference.
 */
static void
pprefs_add_password (struct arglist* pprefs, struct arglist* pref, char* value,
                     GtkWidget* vbox)
{
  GtkWidget *entry, *text, *box;
  char *name = pref->name;
  
  value = get_pref_value(pref, pprefs, value);
  
  box = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
  gtk_widget_show(box);

  text = gtk_label_new(name);
  gtk_box_pack_start(GTK_BOX(box), text, TRUE, TRUE, 5);
  gtk_widget_show(text);

  entry = gtk_entry_new();
  gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
  gtk_entry_set_text(GTK_ENTRY(entry), value);
  gtk_box_pack_end(GTK_BOX(box), entry, TRUE, TRUE, 5);
  gtk_widget_show(entry);
  arg_add_value(pref->value, "ENTRY", ARG_PTR, -1, entry);
}


/**
 * Add "file" widgets corresponding to the "file" plugin preference.
 */
static void
pprefs_add_file (struct arglist* pprefs, struct arglist* pref, char* value,
                 GtkWidget* vbox)
{
  GtkWidget *entry, *text, *box;
  GtkWidget *hbox, *button;
  char *name = pref->name;
  
  value = get_pref_value(pref, pprefs, value);
  
  box = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
  gtk_widget_show(box);

  text = gtk_label_new(name);
  gtk_box_pack_start(GTK_BOX(box), text, TRUE, TRUE, 5);
  gtk_widget_show(text);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_end(GTK_BOX(box), hbox, TRUE, TRUE, 5);
  gtk_widget_show(hbox);

  entry = gtk_entry_new();
  gtk_entry_set_text(GTK_ENTRY(entry), value);
  gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
  gtk_widget_show(entry);
  arg_add_value(pref->value, "ENTRY", ARG_PTR, -1, entry);

  button = gtk_button_new_with_label(_("Select..."));
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5);
  g_signal_connect(GTK_OBJECT(button), "clicked",
      (GtkSignalFunc) select_file, pref->value);

  gtk_widget_show(button);
}


/**
 * Add radio buttons corresponding to the "radio" plugin preference.
 */
static void
pprefs_add_radio (struct arglist* pprefs, struct arglist* pref, char* value,
                  GtkWidget* vbox)
{
  GtkWidget *orig;
  GtkWidget *button, *first_button;
  GtkWidget *label;
  char *t;
  GSList *list = NULL;
  char* def = NULL;
  
  def = get_pref_value(pref, pprefs, def);

  label = gtk_label_new(pref->name);
  gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
  gtk_widget_show(label);

  t = strchr(value, ';');
  if(t)
    t[0] = '\0';
  first_button = orig = gtk_radio_button_new_with_label(NULL, value);
  gtk_box_pack_start(GTK_BOX(vbox), orig, FALSE, FALSE, 5);
  gtk_object_set_data(GTK_OBJECT(orig), "name", value);
  gtk_widget_show(orig);
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(orig), TRUE);
  value = t + sizeof(char);
  if(t)
    while(value)
    {
      if((t = strchr(value, ';')))
	t[0] = '\0';
      button =
	  gtk_radio_button_new_with_label(gtk_radio_button_group
	  (GTK_RADIO_BUTTON(orig)), value);
      gtk_object_set_data(GTK_OBJECT(button), "name", value);
      if(def && !strcmp(def, value))
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
      gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 5);
      gtk_widget_show(button);
      if(t)
	value = t + sizeof(char);
      else
	value = NULL;
    }
  list = gtk_radio_button_group(GTK_RADIO_BUTTON(orig));

  arg_add_value(pref->value, "RADIOBUTTONS", ARG_PTR, -1, list);
}


/**
 * Add a "checkbox" widget corresponding to the "checkbox" plugin preference.
 */
static void
pprefs_add_checkbox (struct arglist* pprefs, struct arglist* pref, char* value,
                     GtkWidget* vbox)
{
  GtkWidget *box;
  GtkWidget *button;
  char *name = pref->name;
  char *def = NULL;

  def = get_pref_value(pref, pprefs, def);
      
  box = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
  gtk_widget_show(box);

  button = gtk_check_button_new_with_label(estrdup(name));
  gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 5);
  gtk_widget_show(button);
  if(def)
  {
    if(!strcmp(def, "yes"))
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
    else
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
  }
  else
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), !strcmp(value, "yes"));
  arg_add_value(pref->value, "CHECKBOX", ARG_PTR, -1, button);
}
#endif
