 /*---[ razorback.c ]-----------------------------------------------
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * The main application file
 *
 * Based on firestarter by Tomas Junnonen (majix@sci.fi)
 *--------------------------------------------------------------------*/

#include <config.h>
#include <gnome.h>

#include <status-docklet.h>

#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

#include "razorback.h"
#include "menus.h"
#include "parse.h"
#include "preferences.h"

#include "xpm/statusrunning.xpm"
#include "xpm/statuscaught.xpm"
#include "xpm/statustopped.xpm"

#include <libgnomeui/gnome-window-icon.h>

#define	XPM_RUNNING	0
#define	XPM_STOPPED	1
#define XPM_CAUGHT	2

gboolean running = FALSE;

GtkWidget *window;
GtkWidget *panelvbox;
GtkWidget *panelpic;

//void set_panel_pic (gchar *pixmap[]);
void set_panel_pic (int pixmapnumber);

/* [ appbar_update ]
 * Updates the application status bar with the state of razorback
 */
static void
appbar_update (void)
{
	gnome_appbar_set_default (GNOME_APPBAR (appbar),
				 _("Razorback running"));
}

/* [ clist_row_select ]
 * Attach current line data to a clist. Uggly.
 */
static void
clist_row_select (GtkWidget *clist, gint row, gint column,
		  GdkEventButton *event, gpointer data)
{
	gint *clistrow = GINT_TO_POINTER (row+1);
	gint *clistcol = GINT_TO_POINTER (column+1);

	gtk_object_set_data (GTK_OBJECT (clist), "row", clistrow);
	gtk_object_set_data (GTK_OBJECT (clist), "col", clistcol);

	if (clist != hitlogclist)
		dynclist =  (gpointer *) clist;
	else return;

	if (clist != denyallclist)
		gtk_clist_unselect_all (GTK_CLIST (denyallclist));
	if (clist != allowallclist)
		gtk_clist_unselect_all (GTK_CLIST (allowallclist));
	if (clist != allowsmclist)
		gtk_clist_unselect_all (GTK_CLIST (allowsmclist));
	if (clist != allowsaclist)
		gtk_clist_unselect_all (GTK_CLIST (allowsaclist));
}

/* [ clist_row_unselect ]
 * Clear current line data from a clist
 */
static void
clist_row_unselect (GtkWidget *clist)
{
	if (clist == (GtkWidget *)dynclist)
		dynclist = NULL;

	gtk_object_set_data (GTK_OBJECT (clist), "row", NULL);
	gtk_object_set_data (GTK_OBJECT (clist), "col", NULL);
}

/* [ hide_window_cb ]
 * Hides the main window, either when "destroyed" or docklet clicked.
 */
static void
hide_window_cb (GtkWidget *window, GdkEventButton* bevent)
{
	if (bevent->button != 3) {
		if (GTK_WIDGET_VISIBLE (window))
			gtk_widget_hide (window);
		else
			if (GTK_WIDGET_VISIBLE (hitlogclist))
				gtk_widget_show (window);
			else
				gtk_widget_show_all (window);
		if (running)
			set_panel_pic(XPM_RUNNING);
			//set_panel_pic (statusrunning_xpm);
	}
}

/* [ clear_hitlist ]
 * Clears the log CList
 */
void
clear_hitlist (void)
{
	if (running)
		set_panel_pic (XPM_RUNNING);
		//set_panel_pic (statusrunning_xpm);
	gtk_clist_clear (GTK_CLIST (hitlogclist));
	clist_row_unselect (hitlogclist);
}

static void
tail_and_resize (GtkWidget *clist)
{
	// gint i/*, width*/;
	gint i=0;

	// I've removed this feature, as users with small screen sizes
	// have problems when large column widths overflow their screen.
	/* Optimize the clists column widths */
	// for (i = 0; i < 4; i++) {
	//	width = gtk_clist_optimal_column_width (GTK_CLIST (hitlogclist), i);
	//	gtk_clist_set_column_width (GTK_CLIST (hitlogclist), i, width);
	//}

	/* Move to the end of the clist */
	while (gtk_clist_get_text (GTK_CLIST (hitlogclist), i, 0, NULL))
		i++;
	gtk_clist_moveto (GTK_CLIST (hitlogclist), i-1, 0, 0.0, 0.0);
	gtk_widget_queue_resize	(window);
}

/* [ poll_log_timeout ]
 * Polls the logfile every 700ms for change, if change, parse lines
 */
int poll_log_timeout (gpointer *fullfile)
{
	char buf[512];

	static long filepointer=-1;
	gchar *logpath = gnome_config_get_string (
		"/razorback/Files/logfile=/var/log/secure");
	int found=0;
	FILE * file;


	if((long)*fullfile == (long)1) {
		filepointer=0;
	}

	file = fopen (logpath, "r");
	if(!file){ g_free(logpath); return(FALSE); }
	if(filepointer==-1)
	{
		// Jump to the end of the file
		fseek(file,0,SEEK_END);
	} else {
		if(fseek(file,filepointer,SEEK_SET)) {
			// Could not seek to the predetermined file position.
			// This probably means that the file has been rotated.
			fseek(file,0,SEEK_SET);
		}
	}

	found=0;
	while(fgets (buf, 512, file) != NULL) {
		if ( parse_hit_log (buf)) {
			found=1;
		}
	}
	if(found) {
		set_panel_pic(XPM_CAUGHT);
		//set_panel_pic (statuscaught_xpm);
		tail_and_resize (hitlogclist);
	}

	filepointer=ftell(file);
	fclose(file);

	g_free(logpath);

	return TRUE;
}

/* [ reload_hitlist ]
 * Reads the entire log file into the hitlogclist
 */
void
reload_hitlist (gpointer data)
{
	long fullfile=1;

	gtk_clist_freeze (GTK_CLIST (hitlogclist));
	gtk_clist_clear (GTK_CLIST (hitlogclist));
	poll_log_timeout ((gpointer)&fullfile);
	gtk_clist_thaw (GTK_CLIST (hitlogclist));
}


/* [ show_about ]
 * Creates the about dialog
 */
void
show_about (GtkWidget *widget, gpointer data)
{
	static GtkWidget *dialog = NULL;

	if (dialog != NULL) {
		g_assert (GTK_WIDGET_REALIZED (dialog));
		gdk_window_show (dialog->window);
		gdk_window_raise (dialog->window);
	}
	else {
		const gchar *authors[] = {"InterSect Alliance Pty Ltd",
					  NULL};

		dialog = gnome_about_new (
			"Razorback", VERSION,
			"InterSect Alliance Pty Ltd - www.intersectalliance.com", authors,
			_("SNORT monitoring system for GNOME.\n"
			"Based loosely on firestarter by Tomas Junnonen"),
			NULL);

		gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
			GTK_SIGNAL_FUNC (gtk_widget_destroyed), &dialog);

		gtk_widget_show (dialog);
		//g_free(authors);
	}
}

/* [ set_panel_pic ]
 * Sets the picture on the docklet to *pixmap
 */
void set_panel_pic (int pic)
{
	static int currentpic=-1;

	if(currentpic == pic) return;
	if(currentpic>-1) {
		gtk_container_remove (GTK_CONTAINER (panelvbox), panelpic);
	}
	currentpic=pic;

	// Note that this is a small memory leak - there must be a better way
	// of swapping pixmaps.

	if(pic == XPM_RUNNING) {
		panelpic = gnome_pixmap_new_from_xpm_d (statusrunning_xpm);
	} else if (pic == XPM_STOPPED) {
		panelpic = gnome_pixmap_new_from_xpm_d (statustopped_xpm);
	} else if (pic == XPM_CAUGHT) {
		panelpic = gnome_pixmap_new_from_xpm_d (statuscaught_xpm);
	}
		
	gtk_box_pack_end (GTK_BOX (panelvbox), panelpic, FALSE, FALSE, 2);
	gtk_widget_show (panelpic);
}

/* [ status_docklet_build_plug ]
 * Builds the docklet plug
 */
static void
status_docklet_build_plug (StatusDocklet *docklet, GtkWidget *plug)
{
	GtkWidget *eventbox;

	g_return_if_fail (docklet != NULL);
	g_return_if_fail (IS_STATUS_DOCKLET (docklet));

	eventbox = gtk_event_box_new ();
	gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK);
	gtk_signal_connect_object (GTK_OBJECT (eventbox), "button_press_event",
				   GTK_SIGNAL_FUNC (hide_window_cb),
				   GTK_OBJECT (window));
	gtk_container_add (GTK_CONTAINER (plug), eventbox);

	panelvbox = gtk_vbox_new (FALSE, 5);
	gtk_container_add (GTK_CONTAINER (eventbox), panelvbox);

	if (gnome_config_get_bool ("/razorback/Program/shutdown=FALSE")) {
		set_panel_pic(XPM_STOPPED);
		running = FALSE;
	}
	else {
		set_panel_pic(XPM_RUNNING);
		running = TRUE;
	}

	install_docklet_menu (eventbox);

	gtk_widget_show_all (plug);
}

/* [ pack_clist ]
 * Convenience function to pack a clist into a scrolled window and box
 */
static void
pack_clist (GtkWidget *clist, GtkWidget *box)
{
	GtkWidget *scrolled_win = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
					GTK_POLICY_NEVER,
					GTK_POLICY_AUTOMATIC);

	gtk_container_add (GTK_CONTAINER (scrolled_win), clist);

	gtk_box_pack_start (GTK_BOX (box), scrolled_win, TRUE, TRUE, 0);
}

/* [ prepare_clist ]
 * Convinience function that installs popups, signals and packs the clists
 */
static void
prepare_clist (GtkWidget *clist, GtkWidget *vbox)
{
	install_clist_popup (clist);

	gtk_signal_connect (GTK_OBJECT (clist), "select-row",
			    GTK_SIGNAL_FUNC (clist_row_select), NULL);
	gtk_signal_connect (GTK_OBJECT (clist), "unselect-row",
			    GTK_SIGNAL_FUNC (clist_row_unselect), NULL);

	if (clist == hitlogclist)
		gtk_signal_connect (GTK_OBJECT (clist), "button_press_event",
				    GTK_SIGNAL_FUNC (popup_menu), hitpopup);
	else {
		gtk_signal_connect (GTK_OBJECT (clist), "button_press_event",
				    GTK_SIGNAL_FUNC (popup_menu), dynlistpopup);
		pack_clist (clist, vbox);
	}
}

/* [ exit_razorback ]
 * Quit razorback
 */
void
exit_razorback (void)
{
	gtk_main_quit ();
}

/* [ closewindow ]
 * Quit or hide, determined by the config files
 */
void
closewindow (void)
{
	if (gnome_config_get_bool ("/razorback/Program/exitonclose=FALSE"))
		exit_razorback ();
	else
		gtk_widget_hide (window);
}

/* [ main ]
 * The main function, this is where it all begins
 */
int
main (int argc, char* argv[])
{
	GtkObject *status_dock;

	GtkWidget *notebook;
	GtkWidget *scrolled_win;
	GtkWidget *tablabel;

	gchar *hitlogtitles[3];

	gchar *logpath;
	FILE *file;
	CORBA_Environment ev;
	CORBA_ORB orb;

	bindtextdomain (PACKAGE, GNOMELOCALEDIR);
	textdomain (PACKAGE);

	hitlogtitles[0] = _("Date/Time");
	hitlogtitles[1] = _("Event");
	hitlogtitles[2] = _("Source/Dest");

	CORBA_exception_init (&ev);
	orb = gnome_CORBA_init (PACKAGE, VERSION, &argc, argv, 0, &ev);

	window = gnome_app_new (PACKAGE, "Razorback");

	ttips = gtk_tooltips_new ();

/* Set up the docklet */
	status_dock = status_docklet_new ();

	gtk_signal_connect (GTK_OBJECT (status_dock), "build_plug",
			    GTK_SIGNAL_FUNC (status_docklet_build_plug), NULL);
	status_docklet_run (STATUS_DOCKLET (status_dock));

/* Set up the window */
	gtk_signal_connect (GTK_OBJECT (window), "delete_event",
			    GTK_SIGNAL_FUNC (closewindow), NULL);

	gnome_window_icon_set_from_file (GTK_WINDOW (window),
					 "/usr/share/pixmaps/razorback.png");

	install_menus_and_toolbar (window);

	notebook = gtk_notebook_new ();
	gnome_app_set_contents (GNOME_APP (window), notebook);

/* set up hitlogclist */
	scrolled_win = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
					GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);

	tablabel = gtk_label_new (_("Razorback hits"));
	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled_win,
				  tablabel);

	hitlogclist = gtk_clist_new_with_titles (3, hitlogtitles);
	prepare_clist (hitlogclist, NULL);
	gtk_container_add (GTK_CONTAINER (scrolled_win), hitlogclist);

	logpath = gnome_config_get_string (
		"/razorback/Files/logfile=/var/log/secure");

	/* Check if user has read access to the log file */
	if ((file=fopen (logpath, "r")) == NULL) {
		gchar *string;
		GtkWidget *dialog;
	
		string = g_strjoin (NULL, _("Logfile \""), logpath, _(
			"\" not found or access denied.\n"
			"Razorback log monitoring disabled.\n\n"
			"You can configure the path to the log file in the "
			"\"preferences\" section."),  NULL);
		dialog = gnome_error_dialog (string);
		gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
		gtk_widget_set_sensitive (GTK_WIDGET (hitlogclist), FALSE);

		g_free (string);
	}
	else {
		long reset=0;
		fclose(file);

		// Every 10 seconds
		gtk_timeout_add (10000, (GtkFunction)poll_log_timeout, (gpointer)&reset);
	}

	appbar_update ();

	if (!gnome_config_get_bool ("/razorback/Program/starthidden=FALSE"))
		gtk_widget_show_all (window);

	gtk_main ();

	CORBA_exception_free (&ev);

	return 0;
}
