/* Nessus
 * Copyright (C) 1998, 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>

#include "../nessus_i18n.h"
#include "context.h"

#include "error_dialog.h"

#ifdef USE_GTK
#include <gtk/gtk.h>

/*
 * Reset the rules tree to the three default groups
 */
static void reset_rules_tree(ctrls)
  struct arglist * ctrls;
{
  GtkWidget * tree;
  GtkTreeStore * store;
  GtkTreeIter iter;

  tree = arg_get_value(ctrls, "RULES_TREE");
  store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree)));

  gtk_tree_store_clear(store);
  /* fill the tree with the 3 default groups */
  gtk_tree_store_append(store, &iter, NULL);  /* Acquire a top-level iterator */
  gtk_tree_store_set(store, &iter, 0,
    _("Server rules (priority over user rules): can not be shown"), -1);
  gtk_tree_store_append(store, &iter, NULL);  /* Acquire a top-level iterator */
  gtk_tree_store_set(store, &iter, 0,
    _("Serverside user rules (priority over clientside user rules)"), -1);
  gtk_tree_store_append(store, &iter, NULL);  /* Acquire a top-level iterator */
  gtk_tree_store_set(store, &iter, 0,
    _("Clientside user rules"), -1);
}

/*
 * Fill the treelist widget of the rules page
 * with the serverside user rules and with the
 * clientside user rules.
 */
void fill_rules_list(context, ctrls)
  struct context *context;
  struct arglist *ctrls;
{
  GtkWidget * tree;
  GtkTreeStore * store;
  GtkTreePath * path;
  GtkTreeIter iter, parent;
  struct arglist * p = arg_get_value(context->prefs, "SERVER_PREFS");
  char * rule;

  /* first of all, reset the tree */
  reset_rules_tree(ctrls);

  if (!p)
    return; /* something is not in order */

  /* get the objects */
  tree = arg_get_value(ctrls, "RULES_TREE");
  store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree)));

  /* go to the second entry */
  path = gtk_tree_path_new_from_string("1");
  gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &parent, path);

  /* append the serverside user rules */
  p = arg_get_value(p, "RULES");
  while (p && p->next)
  {
    rule = emalloc(strlen((char *)p->value)+1);
    strcpy(rule, p->value);
    gtk_tree_store_append(store, &iter, &parent);
    gtk_tree_store_set(store, &iter, 0, rule, -1);
    p = p->next;
  }
  gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE);
  gtk_tree_path_free(path);

  /* go to the third entry */
  path = gtk_tree_path_new_from_string("2");
  gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &parent, path);
  gtk_tree_path_free(path);

  /* append the clientside user rules */
  p = arg_get_value(context->prefs, "CLIENTSIDE_USERRULES");
  while (p && p->next)
  {
    rule = emalloc(strlen((char *)p->value)+1);
    strcpy(rule, p->value);
    gtk_tree_store_append(store, &iter, &parent);
    gtk_tree_store_set(store, &iter, 0, rule, -1);
    p = p->next;
  }

  path = gtk_tree_path_new_from_string("2");
  gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE);
  gtk_tree_path_free(path);
}

/*
 * Check whether the given string is a syntactically correct
 * representation of a subnet.
 * The function returns 1 if it is valid and 0 if not.
 *
 * Subnets are of these forms:
 * n1.n2.n3.n4    (equals to n1.n2.n3.n4/32)
 * n1.n2.n3.n4/x
 * n1.n2.n3.n4/n5.n6.n7.n8
 * with each n in 0..255 and x in 0..32
 */
int
is_valid_subnet_str(subnet)
  char * subnet;
{
  unsigned int n1, n2, n3, n4, n5, n6, n7, n8, x;
  char c;

  if (sscanf(subnet, "%u.%u.%u.%u%c", &n1, &n2, &n3, &n4, &c) == 4)
  {
    if (n1 <= 255 && n2 <= 255 && n3 <= 255 && n4 <= 255)
      return 1;
  }

  if (sscanf(subnet, "%u.%u.%u.%u/%u%c", &n1, &n2, &n3, &n4, &x, &c) == 5)
  {
    if (n1 <= 255 && n2 <= 255 && n3 <= 255 && n4 <= 255 && x <= 32)
      return 1;
  }

  if (sscanf(subnet, "%u.%u.%u.%u/%u.%u.%u.%u%c", &n1, &n2, &n3, &n4,
             &n5, &n6, &n7, &n8, &c) == 8)
  {
    if (n1 <= 255 && n2 <= 255 && n3 <= 255 && n4 <= 255 &&
        n5 <= 255 && n6 <= 255 && n7 <= 255 && n8 <= 255)
      return 1;
  }

  return 0;
}

/*
 * Add a rule as specified through the combo and entry widget to the
 * treestore and treelist.
 */
static void add_rule_callback(w, ctrls)
 GtkWidget * w;
 struct arglist * ctrls;
{
  GtkCombo * action_combo;
  GtkEntry * target_entry;
  GtkWidget * tree;
  GtkTreeStore * store;
  GtkTreeIter iter, parent;
  char * action = NULL;
  char * target = NULL;
  char * rule = NULL;
  GtkTreePath * path;

  action_combo = arg_get_value(ctrls, "ACTION");
  action = (char *)gtk_entry_get_text(GTK_ENTRY(action_combo->entry));
  target_entry = arg_get_value(ctrls, "TARGET");
  target = (char*)gtk_entry_get_text(target_entry);
  tree = arg_get_value(ctrls, "RULES_TREE");
  store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree)));

  if (!strcmp(action, "default accept") ||
      !strcmp(action, "default deny") ||
      !strcmp(action, "default reject"))
  {
    rule = emalloc(strlen(action)+1);
    sprintf(rule, "%s", action);
  } else {
    if (strlen(target) == 0 || ! is_valid_subnet_str(target))
    {
      show_warning(_("The target for this rule must be a valid IP or Subnet.\n"));
      return;
    }
    rule = emalloc(strlen(action)+strlen(target)+1+1);
    sprintf(rule, "%s %s", action, target);
  }

  /* add the rule to the list store */
  path = gtk_tree_path_new_from_string("2");
  gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &parent, path);
  gtk_tree_store_append(store, &iter, &parent); 
  gtk_tree_store_set(store, &iter, 0, rule, -1);

  gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE);
  gtk_tree_path_free(path);
}

/*
 * Remove the rule that is currently selected (provided the selected
 * line is a removable rule.
 */
static void remove_rule_callback(w, ctrls)
 GtkWidget * w;
 struct arglist * ctrls;
{
  GtkWidget * tree;
  GtkTreeStore * store;
  GtkTreePath * path;
  GtkTreeViewColumn *focus_column;
  GtkTreeIter iter, parent;

  tree = arg_get_value(ctrls, "RULES_TREE");
  store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree)));

  /* Get the selected entry */
  gtk_tree_view_get_cursor(GTK_TREE_VIEW(tree), &path, &focus_column);

  if (!path)
    return; /* no line selected */

  gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path);

  /* check whether this is a line that is allowed to be removed */
  gtk_tree_path_free(path);
  path = gtk_tree_path_new_from_string("2"); /* must be under third group */
  gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &parent, path);
  if (!gtk_tree_store_is_ancestor(store, &parent, &iter))
    return; /* no, its not */

  /* remove the line */
  gtk_tree_store_remove(store, &iter);

  gtk_tree_path_free(path);
}

struct arglist *
prefs_dialog_user()
{
  struct arglist * ctrls = emalloc(sizeof(struct arglist));
  GtkWidget * frame;
  GtkWidget * vbox;
  GtkWidget * hbox;
  GtkWidget * label;
  GtkWidget * combo;
  GList * combo_items = NULL;
  GtkWidget * scrolledwindow;
  GtkTreeStore *store;
  GtkWidget *tree;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;
  GtkWidget * button;
  GtkWidget * entry;

  /* The overall frame */
  frame = gtk_frame_new(_("User Access Rules"));
  gtk_container_border_width(GTK_CONTAINER(frame), 10);
  gtk_widget_show(frame);
  arg_add_value(ctrls, "FRAME", ARG_PTR, -1, frame);

  /* the overall vbox that will contain all widgets */
  vbox = gtk_vbox_new(FALSE, 4);
  gtk_container_add(GTK_CONTAINER(frame), vbox);
  gtk_widget_show(vbox);

  /* The hbox that contains the selection elements for the rule */
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);

  /* The label for action entry */
  label = gtk_label_new(_("Action:"));
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  gtk_widget_show(label);

  /* A combo selection for the action */
  combo_items = g_list_append(combo_items, "accept");
  combo_items = g_list_append(combo_items, "reject");
  combo_items = g_list_append(combo_items, "deny");
  combo_items = g_list_append(combo_items, "default accept");
  combo_items = g_list_append(combo_items, "default reject");
  combo_items = g_list_append(combo_items, "default deny");
  combo = gtk_combo_new();
  gtk_combo_set_value_in_list(GTK_COMBO(combo), TRUE, FALSE);
  gtk_widget_show(combo);
  gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
  gtk_combo_set_popdown_strings(GTK_COMBO(combo), combo_items);
  arg_add_value(ctrls, "ACTION", ARG_PTR, -1, combo);

  /* The label for target entry */
  label = gtk_label_new(_("Target:"));
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  gtk_widget_show(label);

  /* The entry for the target */
  entry = gtk_entry_new();
  gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
  gtk_widget_show(entry);
  arg_add_value(ctrls, "TARGET", ARG_PTR, -1, entry);

  /* The button to add a rule */
  button = gtk_button_new_with_label(_("Add rule"));
  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
  gtk_signal_connect(GTK_OBJECT(button), "clicked",
                     GTK_SIGNAL_FUNC(add_rule_callback), ctrls);
  gtk_widget_show(button);

  /* The scrolled window where to have the treelist */
  scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
  gtk_box_pack_start(GTK_BOX(vbox), scrolledwindow, TRUE, TRUE, 5);
  gtk_widget_show(scrolledwindow);

  /* A tree store for the specified rules */
  store = gtk_tree_store_new(1, G_TYPE_STRING);

  /* A treelist with the rules */
  tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
  g_object_unref(G_OBJECT(store));
  gtk_container_add(GTK_CONTAINER(scrolledwindow), tree);
  gtk_widget_show(tree);
  arg_add_value(ctrls, "RULES_TREE", ARG_PTR, -1, tree);

  /* fill the tree with defaults */
  reset_rules_tree(ctrls);

  /* The renderer for the treelist */
  renderer = gtk_cell_renderer_text_new();

  /* The column with the rules */
  column = gtk_tree_view_column_new_with_attributes(_("Rules"), renderer,
                                                    "text", 0,
                                                    NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);

  /* The hbox that contains the remove button */
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);

  /* The button to remove a rule */
  button = gtk_button_new_with_label(_("Remove rule"));
  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
  gtk_signal_connect(GTK_OBJECT(button), "clicked",
                     GTK_SIGNAL_FUNC(remove_rule_callback), ctrls);
  gtk_widget_show(button);

  return(ctrls);
}
#endif
