/* $Id: prefs_scan_assistant.c,v 1.1.1.1.2.1 2006/02/14 20:58:22 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 "error_dialog.h"
#include "context.h"
#include "prefs_context.h"
#include "preferences.h"
#include "prefs_scope_tree.h"
#include "prefs_dialog.h"
#include "prefs_dialog_auth.h"

struct scan_assistant {
  GtkWindow *parent;
  GtkWidget *dialog;
  GtkWidget *notebook;
  GtkWidget *task_name;
  GtkWidget *task_comment;
  GtkWidget *scope_name;
  GtkWidget *scope_comment;
  GtkWidget *targets;
  GtkWidget *next;
};


void
scan_assistant_add_text(box, text)
  GtkWidget *box;
  const gchar *text;
{
  GtkWidget *label;

  label = gtk_label_new(text);
  gtk_widget_show(label);
  gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);

  gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
}

GtkWidget *
scan_assistant_add_entry(box, name, text)
  GtkWidget *box;
  const gchar *name;
  const gchar *text;
{
  GtkWidget *vbox;
  GtkWidget *entry;

  vbox = gtk_vbox_new(FALSE, 2);
  gtk_widget_show(vbox);

  scan_assistant_add_text(vbox, name);

  entry = gtk_entry_new();
  gtk_widget_show(entry);
  gtk_box_pack_start_defaults(GTK_BOX(vbox), entry);
  gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
  g_object_set_data(G_OBJECT(entry), "default", (gpointer)text);

  gtk_box_pack_start(GTK_BOX(box), vbox, FALSE, FALSE, 0);

  return entry;
}

GtkWidget *
scan_assistant_add_textview(box, name)
  GtkWidget *box;
  const gchar *name;
{
  GtkWidget *frame;
  GtkWidget *scrolledwindow;
  GtkWidget *textview;

  frame = gtk_frame_new(name);
  gtk_widget_show(frame);

  scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
  gtk_widget_show(scrolledwindow);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
      GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
  gtk_container_set_border_width(GTK_CONTAINER(scrolledwindow), 4);
  gtk_container_add(GTK_CONTAINER(frame), scrolledwindow);

  textview = gtk_text_view_new();
  gtk_widget_show(textview);
  gtk_container_add(GTK_CONTAINER(scrolledwindow), textview);

  gtk_box_pack_start_defaults(GTK_BOX(box), frame);
  return textview;
}

void
scan_assistant_apply_comment(assistant, textview, ctrls)
  struct scan_assistant *assistant;
  GtkWidget *textview;
  struct arglist *ctrls;
{
  GtkTextBuffer *assistant_buffer, *comment_buffer;
  GtkTextIter start, end;
  gchar *text;

  assistant_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
  comment_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
	arg_get_value(arg_get_value(ctrls, "COMMENT"), "COMMENT")));
  gtk_text_buffer_get_bounds(assistant_buffer, &start, &end);
  text = gtk_text_buffer_get_text(assistant_buffer, &start, &end, FALSE);
  gtk_text_buffer_set_text(comment_buffer, text, -1);
  g_free(text);
}

GtkWidget *
scan_assistant_append_page(notebook, name)
  GtkWidget *notebook;
  const gchar *name;
{
  GtkWidget *vbox;
  GtkWidget *label;

  vbox = gtk_vbox_new(FALSE, 15);
  gtk_widget_show(vbox);
  gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);

  label = gtk_label_new(name);
  gtk_widget_show(label);

  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);
  return vbox;
}

void
scan_assistant_update_nextbutton(assistant, page_num)
  struct scan_assistant *assistant;
  guint page_num;
{
  static gint previous = GTK_RESPONSE_NONE;
  gint response;
  const gchar *stock;

  if(page_num < 3)
  {
    response = GTK_RESPONSE_ACCEPT;
    stock = GTK_STOCK_GO_FORWARD;
  }
  else
  {
    response = GTK_RESPONSE_OK;
    stock = GTK_STOCK_EXECUTE;
  }

  if(response != previous)
  {
    if(assistant->next)
      gtk_widget_destroy(assistant->next);

    assistant->next = gtk_dialog_add_button(
	GTK_DIALOG(assistant->dialog), stock, response);
    gtk_dialog_set_default_response(GTK_DIALOG(assistant->dialog), response);
  }
}

void
scan_assistant_setdefault(widget)
  GtkWidget *widget;
{
  if(GTK_IS_ENTRY(widget) && !gtk_entry_get_text(GTK_ENTRY(widget))[0])
    gtk_entry_set_text(GTK_ENTRY(widget),
	g_object_get_data(G_OBJECT(widget), "default"));
}

void
scan_assistant_update_page(assistant, page_num)
  struct scan_assistant *assistant;
  guint page_num;
{
  GtkWidget *focus;

  gtk_dialog_set_response_sensitive(GTK_DIALOG(assistant->dialog),
      GTK_RESPONSE_REJECT, page_num > 0);

  scan_assistant_update_nextbutton(assistant, page_num);
  switch(page_num)
  {
    case 0:
      focus = assistant->task_name;
      break;
    case 1:
      focus = assistant->scope_name;
      break;
    case 2:
      focus = assistant->targets;
      break;
    default:
      focus = assistant->next;
  }
  scan_assistant_setdefault(focus);
  gtk_widget_grab_focus(focus);
}

void
scan_assistant_switch_page(notebook, page, page_num, assistant)
  GtkNotebook *notebook;
  GtkNotebookPage *page;
  guint page_num;
  struct scan_assistant *assistant;
{
  scan_assistant_update_page(assistant, page_num);
}

void
scan_assistant_create_dialog(struct scan_assistant *assistant,
    const char * targets)
{
  GtkWidget *dialog;
  GtkWidget *notebook;
  GtkWidget *page;

  assistant->dialog = dialog = gtk_dialog_new_with_buttons(
      _("Scan Assistant"), assistant->parent,
      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
      GTK_STOCK_GO_BACK, GTK_RESPONSE_REJECT,
      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
      NULL);
  gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);

  assistant->notebook = notebook = gtk_notebook_new();
  gtk_widget_show(notebook);
  gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
  gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook);

  /*
   * Step 1
   */
  page = scan_assistant_append_page(notebook, _("Step 1: Task"));
  scan_assistant_add_text(page,
      _("Tasks describe what you want to do. You can use it to logically group"
	"\nyour duties by subject, frequency, location or anything else."
	"\nPossible task names are:"
	"\n - Weekly checks"
	"\n - Customer XYZ"
	"\n - Hosts of project ABC"
	"\nYou should also enter a comment further explaining the task."));
  assistant->task_name = scan_assistant_add_entry(page,
      _("Please enter a name for your task:"), _("unnamed task"));
  assistant->task_comment = scan_assistant_add_textview(page, _("Comment:"));

  /*
   * Step 2
   */
  page = scan_assistant_append_page(notebook, _("Step 2: Scope"));
  scan_assistant_add_text(page,
      _("Scopes are part of a task. Each scope represents a connection"
	"\nto a Nessus Server and a list of hosts to scan."
	"\nPossible scopes names are:"
	"\n - Internet servers (e.g. in task \"Weekly Checks\")"
	"\n - Application servers (e.g. in task \"Customer XYZ\")"
	"\n - Workstations (e.g. in task \"Hosts of project ABC\")"
	"\nYou should also enter a comment further explaining the scope."
	));
  assistant->scope_name = scan_assistant_add_entry(page,
      _("Please enter a name for the current scope:"), _("unnamed scope"));
  assistant->scope_comment = scan_assistant_add_textview(page, _("Comment:"));

  /*
   * Step 3
   */
  page = scan_assistant_append_page(notebook, _("Step 3: Targets"));
  scan_assistant_add_text(page,
      _("Targets are the hosts and networks you want to scan in this scope."
	"\nThey can be entered in the following formats:"
	"\n - simple hostname (for hosts in your LAN)"
	"\n - fully qualified host name (e.g. www.example.com)"
	"\n - IP adress (e.g. 192.168.0.1)"
	"\n - IP network (e.g. 192.168.0.0/24 or 192.168.0.0/255.255.255.0)"
	"\nYou can enter several targets by separating them with a comma."
	));
  assistant->targets = scan_assistant_add_entry(page,
      _("Please enter the targets to scan:"), targets);
  scan_assistant_add_text(page,
      _("Warning: Please make sure you are allowed to scan these hosts!"
	"\nHarmful checks are disabled by default, but some computers and"
	"\nespecially print servers have bugs which might crash them."
	"\nConsider getting a written permission before scanning important"
	"\nservers which are in production."
	));

  /*
   * Step 4
   */
  page = scan_assistant_append_page(notebook, _("Step 4: Execute"));
  scan_assistant_add_text(page,
      _("You are now ready to start the scan."
	"\n\nWhen clicking on \"Execute\", the connection dialog will appear."
	"\nYou have to choose a Nessus Server which can reach the targets"
	"\nyou have specified in the previous step."
	"\n\nNote: You need to have an account on this server. Contact your"
	"\nsystem administrator if you don't have one yet."
	"\n\nThe scan progress window will inform you about the current"
	"\nstatus and allows you to stop scanning a single host or abort"
	"\nthe whole scan."
	"\n\nTo repeat this scan, select the scope which will be created now,"
	"\nand choose \"Execute\" from the toolbar or the \"Scope\" menu."
	"\nEach scan will create a separate new report under the current Scope."
	));

  scan_assistant_update_page(assistant, 0);
}

void
prefs_scan_assistant(widget, ctrls)
  GtkWidget *widget;
  gpointer ctrls;
{
  void *context_window = arg_get_value(ctrls, "CONTEXT");
  struct scan_assistant *assistant = emalloc(sizeof(struct scan_assistant));
  gint response = GTK_RESPONSE_NONE;
  gulong handler;

  assistant->parent = context_window?GTK_WINDOW(context_window):NULL;
  scan_assistant_create_dialog(assistant, prefs_get_string(Global, "targets"));

  handler = g_signal_connect_after(G_OBJECT(assistant->notebook),
      "switch-page", G_CALLBACK(scan_assistant_switch_page), assistant);

  arg_set_value(ctrls, "CONTEXT", -1, assistant->dialog);
  while(response != GTK_RESPONSE_CANCEL && response != GTK_RESPONSE_OK)
    switch((response = gtk_dialog_run(GTK_DIALOG(assistant->dialog))))
    {
      case GTK_RESPONSE_REJECT:
	gtk_notebook_prev_page(GTK_NOTEBOOK(assistant->notebook));
	break;
      case GTK_RESPONSE_ACCEPT:
	gtk_notebook_next_page(GTK_NOTEBOOK(assistant->notebook));
	break;
      case GTK_RESPONSE_OK:
	break;
      default:
	response = GTK_RESPONSE_CANCEL;
    }

  arg_set_value(ctrls, "CONTEXT", -1, assistant->parent);
  g_signal_handler_disconnect(G_OBJECT(assistant->notebook), handler);
  gtk_widget_hide(assistant->dialog);

  if(response == GTK_RESPONSE_OK)
  {
    struct context *context;

    scan_assistant_setdefault(assistant->task_name);
    scan_assistant_setdefault(assistant->scope_name);
    scan_assistant_setdefault(assistant->targets);

    /* Task */
    prefs_context_update(scopetree_new(CONTEXT_TASK,
	  gtk_entry_get_text(GTK_ENTRY(assistant->task_name)), NULL));
    scan_assistant_apply_comment(assistant, assistant->task_comment, ctrls);

    /* Scope */
    context = scopetree_new(CONTEXT_SCOPE,
	gtk_entry_get_text(GTK_ENTRY(assistant->scope_name)), NULL);
    gtk_entry_set_text(
	GTK_ENTRY(arg_get_value(arg_get_value(ctrls, "TARGET"), "TARGET")),
	gtk_entry_get_text(GTK_ENTRY(assistant->targets)));
    prefs_context_update(context);
    scan_assistant_apply_comment(assistant, assistant->scope_comment, ctrls);

    /* Execute */
    if(prefs_dialog_auth_connect_dialog(context, ctrls))
      prefs_dialog_execute(NULL, ctrls);
  }

  gtk_widget_destroy(assistant->dialog);
  efree(&assistant);
}
