#!/usr/bin/perl -w
use Curses::UI;
use strict;

use SM::Conf;

##################################################
# some sub definitions
##################################################

sub exit_question;
sub back_to_menu;
sub sel_callback;
sub status_text;
sub footer_text;
sub data_timer_callback;
sub timer_update;
sub check_pid;
sub sort_stats;
sub serious_error;
sub ban_dlg;
sub unban_question;
sub ban_ip;
sub unban_ip;
sub view_ip;

##################################################
# move this in the config section kusi!
##################################################

# pipes for commands and data
#my $pipe_cmd  = "/tmp/gui2scrut";	# tell scrutinizer which data set
#my $pipe_data = "/tmp/scrut2gui";	# get the data set

my $pipe_cmd = $SM::Conf::PIPE_HANDLE_G2S;
my $pipe_data = $SM::Conf::PIPE_HANDLE_S2G;

# the pid file for signal handling
#my $pid_file  = "/var/run/scrutinizer.pid";	# need to know the pid for sending signals

my $pid_file = $SM::Conf::PID_FILE;

##################################################
# global vars
##################################################

# define the screen numbers
my $STAT  = 0;
my $IP	  = 1;
my $BLACK = 2;

# keep the current screen
my $cur_scr = $STAT;

# define data format
my $stat_format			= 
		"@>>>>>>>>>>>>>>>  @>>>>>>>  @>>>>>>>>> @>>>>>>>>  @>>>>>>>  @>>>>>>>>";
my $stat_title_format	= 
		"@|||||||||||||||  @|||||||  @||||||||| @||||||||  @|||||||  @||||||||"; 
my $black_format		= 
		"@>>>>>>>>>>>>>>>  @>>>>>>>>> secs  @>>>>>>>>>>>>>>";
my $black_title_format	= 
		"@|||||||||||||||  @||||||||||||||  @||||||||||||||";
my $ip_format			= 
		"@<<<<<<<<<<<<<<<<<<<<<<<<<<<<  @>>>>  @>>>>>>  @>>>>>>>>>>>>>>>>>>>>>>>>>";
my $ip_title_format		= 
		"@||||||||||||||||||||||||||||  @||||  @||||||  @|||||||||||||||||||||||||";

# define the titles
my @stat_titles=("IP", "Alert", "Av. short", "Av. long", "Periods", "Requests");
my @black_titles=("IP Address", "Time Remaining", "Blocking Level");
my @ip_titles=("URL", "Hits", "Params", "Last visit");

# define the sorted column in the stats
my $stat_sorted_col = 2; # 2 -> alert

# define status text for stats
my %sorting = (
	'2' => 'Alert',
	'3' => 'Short Time Average',
	'4' => 'Long Time Average',
	'5' => 'Number of Periods',
	'6' => 'Number of Requests'
);

##################################################
# the root object
##################################################

# create the root object
my $cui = new Curses::UI (
	-clear_on_exit => 1,
);

##################################################
# prepare the pipes 
##################################################

# if pipe not exist
if (!-p $pipe_cmd) {
	# create a new one
	if ( system('mkfifo', $pipe_cmd ) ) {
		serious_error("mkfifo $pipe_cmd failed: $!"); 
	}
}

# if pipe not exist
if (!-p $pipe_data) {
	# create a new one
	if ( system('mkfifo', $pipe_data) ) {
		serious_error("mkfifo $pipe_data failed: $!"); 
	}
}

##################################################
# get the pid of scrutinizier.pl
##################################################

# check for the pid-file
check_pid();
# open the pid file
if ( !(open(PID_FH, $pid_file)) ) {
	serious_error("couldn't open $pid_file: $!");
}

my $scrutinizer_pid;

# read the pid of scrutinizier.pl
while (<PID_FH>) {
	$scrutinizer_pid = $_;
}

##################################################
# header & footer
##################################################

# define head window
my $head = $cui->add(
	undef , 'Window',
	-height	=> 1,
);

# define header
$head->add(
	undef , 'Label',
	-width	=> -1,
	-textalignment => 'middle',
	-text	=> 'scrutinizer command console',
);

# define foot window
my $foot = $cui->add(
	undef , 'Window',
	-height => 1,
	-y 	=> -1,
);	

# define footer
$foot->add(
	'footer', 'Label',
	-width => $cui->width / 2,
	-text	=> "",
);

# set footer text
footer_text("(^Q)uit, (^S)tatistics, Bl(^A)cklist");

# define status text
$foot->add(
	'status', 'Label',
	-x	=> -1, 
	-textalignment => 'right',
	-width => $cui->width / 2,
	-text	=> "",
);

##################################################
# create the main screens
##################################################

# define titles
my %screens = (
	 '0' => 'Statistical Data',
	 '1' => 'Request Details',
	 '2' => 'Blacklist',
);

my @screens = sort {$a<=>$b} keys %screens;

# define common args
my %args = (
	-border	=> 1,
	-titlereverse => 0,
	-y	=> 1,
	-height => $cui->height - 2,
	-ipadtop => 1,
	-ipadleft => 1,
	-ipadright => 1,
	-ipadbottom => 1,
);

# define hash
my %scr;

# add the windows
while (my ($nr, $title) = each %screens)
{
	my $id = "screen_$nr";
	$scr{$nr} = $cui->add(
		$id, 'Window',
		-title	=> "$title",
		%args,
	);
}

##################################################
# the statistical data 
##################################################

# set the window heading
$scr{$STAT}->add(
	'stat_label', 'Label',
	-width => -1,
	-text 	=> "Current data from scrutinizer",
);

# format the titles
$^A="";
formline($stat_title_format, @stat_titles);

# add the titles on top of the list
$scr{$STAT}->add(
	undef, 'Label',
	-y => 2,
	-x => 2,
	-bold => 1,
	-text => $^A,
);

# define entry array and hash
my @stat_vals;
my %stat_entries;

# add the list
$scr{$STAT}->add(
        'stats', 'Listbox',
        -values    => \@stat_vals,
        -labels    => \%stat_entries,
	-y	=> 3,
	-height => $scr{$STAT}->height - 9,
	-border => 1,
	-hscrollbar => 1,
	-vscrollbar => 1,
	-wraparound => 1,
	-onchange => \&view_ip,
);

# add the sort instructions
$scr{$STAT}->add(
	undef, 'Label',
	-y => $scr{$STAT}->height - 6,
	-text => " sort functions: (a)lert, (s)hort av., (l)ong av., (p)eriods,".
			 " (r)equests\n press enter for detailed request information",
);

# create the timer
# for update every 2 secs
$cui->set_timer(
	'data_timer',
	\&data_timer_callback,
	2,
);
$cui->disable_timer('data_timer');

# default sorting: alert
sort_stats($stat_sorted_col);

# activate timer when window gets active
$scr{$STAT}->onFocus(sub{$cui->enable_timer ('data_timer')} );
# deactivate timer when window gets inactive
$scr{$STAT}->onBlur( sub{$cui->disable_timer('data_timer')} );

##################################################
# the blacklist
##################################################

# add the window heading
$scr{$BLACK}->add(
	'black_label', 'Label',
	-width => -1,
	-text 	=> "Current blacklisted IP\'s",
);

# format the titles
$^A="";
formline($black_title_format, @black_titles);

# add the titles on top of the list
$scr{$BLACK}->add(
	undef, 'Label',
	-y => 2,
	-x => 2,
	-bold => 1,
	-text => $^A,
);

# define entry array and hash
my @black_vals;
my %black_entries;

# add the list
my $blacklist = $scr{$BLACK}->add(
        undef, 'Listbox',
        -values    => \@black_vals,
        -labels    => \%black_entries,
	-y	=> 3,
	-height => $scr{$BLACK}->height - 8,
	-border => 1,
	-hscrollbar => 1,
	-vscrollbar => 1,
	-wraparound => 1,
	-onchange => \&sel_callback,
);

# add the ban/unban instructions
$scr{$BLACK}->add(
	undef, 'Label',
	-y => $scr{$BLACK}->height - 5,
	-text => " functions: (b)lock ip, (u)nblock ip",
);

# set the timer
# update every 2 secs
$cui->set_timer(
	'black_timer',
	\&black_timer_callback,
	2,
);
$cui->disable_timer('black_timer');

# activate timer when window gets active
$scr{$BLACK}->onFocus(sub{$cui->enable_timer ('black_timer')} );
# inactivate timer when window gets inactive
$scr{$BLACK}->onBlur( sub{$cui->disable_timer('black_timer')} );

##################################################
# the ban dialog
##################################################

# create the dialog window
my $ban_dlg = $cui->add(
	'ban_dialog', 'Window', 
	-height=>8, 
	-width=>33, 
	-border=>1, 
	-title=>"add IP to Blacklist",
	-y => $cui->height /2,
	-x => $cui->width  /2,
);


	#-modalfocus => 1,

# add the ip address input field
my $ipaddr = $ban_dlg->add(
        'ipaddr', 'TextEntry',
        -border => 1,
        -width => 18,
        -maxlength => 15,
);

# add the duration input field
my $dur = $ban_dlg->add(
        'duration', 'TextEntry',
        -border => 1,
        -width => 8,
        -maxlength => 5,
        -x => 19,
);

# add the entity label
$ban_dlg->add(
        undef, 'Label',
        -text => 'sec',
        -y => 1,
        -x => 27,
);

my $loc = $ban_dlg->add(
        'location', 'Radiobuttonbox',
        -values    => [0, 1],
        -labels    => { 0 => 'Level I', 
                        1 => 'Level II'},
		-y => 4,
);

# add the buttons
my $button = $ban_dlg->add(
        undef, 'Buttonbox',
        -buttons   => [
        {
                -label => '< yes >',
                -value => 1,
                -shortcut => 1,
				-onpress => (sub{ban_ip($ipaddr->get(),$dur->get(),$loc->get())}),
				},{
				-label => '< no >',
				-value => 2,
				-shortcut => 2,
				-onpress => (sub{$scr{$cur_scr}->focus()}),
				}
				],
				-x => 17,
				-y => 5,
				);

# set cursor to ip address field
$ipaddr->focus();

##################################################
# the ip details 
##################################################

# set the window heading
$scr{$IP}->add(
				'ip_label', 'Label',
				-width => -1,
				-text 	=> 'Current ip data from <undefined>',
			  );

# format the titles
$^A="";
formline($ip_title_format, @ip_titles);

# add the titles on top of the list
$scr{$IP}->add(
				undef, 'Label',
				-y => 2,
				-x => 2,
				-bold => 1,
				-text => $^A,
			  );

# define entry array and hash
my @ip_vals;
my %ip_entries;

# add the list
$scr{$IP}->add(
				undef, 'Listbox',
				-values    => \@ip_vals,
				-labels    => \%ip_entries,
				-y	=> 3,
				-border => 1,
				-hscrollbar => 1,
				-vscrollbar => 1,
				-wraparound => 1,
				-onchange => \&sel_callback,
			  );

##################################################
# implement some subs
##################################################

# the exit function
sub exit_question
{
# disable timers, otherwise the dialog will be
# sent to background after $scr{x}->focus()
		$cui->disable_timer('data_timer') if ($cur_scr == $STAT);
		$cui->disable_timer('black_timer') if ($cur_scr == $BLACK);

# ask user for termination
		my $return = $cui->dialog(
						-message => "Exit Scrutinizer Command Console?",
						-title   => "Terminate Application",
						-buttons => ['yes', 'no'],
						);

		if ($return) {
# delete the pipes
				unlink($pipe_cmd);
				unlink($pipe_data);
# exit the program
				exit(0);
		}
}

sub go_blacklist
{
# change active screen
		$cur_scr = $BLACK;
# disable timer if in statistic window
		$cui->disable_timer('data_timer') if ($cur_scr == $STAT);
# set status text
		status_text("The Blacklist");
# make blacklist visible
		$scr{$cur_scr}->focus();
}

sub go_statistic
{
# change active screen
		$cur_scr = $STAT;
# disable timer if in blacklist window
		$cui->disable_timer('black_timer') if ($cur_scr == $BLACK);
# set status text
		status_text("sorted by: $sorting{$stat_sorted_col}");
# make statistic window visible
		$scr{$cur_scr}->focus();
}

# the menu function
sub back_to_menu
{
# only exit when not in detail view 
		if ($cur_scr != $IP) {
				exit_question();
		} else {
# go back to statistic
				go_statistic();
		}
}

sub sel_callback
{
# get the argument
		my $listbox = shift;
# clear the selection
		$listbox->clear_selection();
# go back to actual screen
		$scr{$cur_scr}->focus();
}

sub status_text
{
# set the status text
		$foot->getobj('status')->text(shift);
}

sub footer_text
{
# set the footer text
		$foot->getobj('footer')->text(shift);
}

sub data_timer_callback
{
# check if scrutinizer.pl is still running
# if not, terminate otherwise kill will block
		check_pid();

# send the update signal
		kill USR1 => $scrutinizer_pid;

# open the cmd-pipe
		if ( !(open(CMD, "> $pipe_cmd")) ) {
				serious_error("Couldn't open $pipe_cmd");
		}

# write the request to the cmd-pipe
		print CMD "stat\n";

# close the cmd-pipe
		close(CMD);

		my @unsorted_tab;
		my @sorted_tab;
		my $line_counter = 0;
		my $position;
		my @sorted_vals;
		my @data_line;

# open the data-pipe
		if ( !(open(DATA, "< $pipe_data")) ) {
				serious_error("Couldn't open $pipe_data");
		}


# read the table from the data-pipe
		while (<DATA>)
		{
# create a 2D array from the string in one line !
				push @unsorted_tab, [split];
# increment line counter
				$line_counter++;
		}

# close the data-pipe
		close(DATA);

# sort the table
		@sorted_tab = sort { 
			@{$b}[$stat_sorted_col] <=> @{$a}[$stat_sorted_col] 
		} @unsorted_tab;

# define $line variable
		my $line;

# format each line and save it
# into the temporary hash
		foreach $line (@sorted_tab)
		{
# clear the accumulator
				$^A="";
# read the dataline
				@data_line = @{$line};
# save the position
				$position = shift(@data_line);
# copy position into value-array of listbox
				push @sorted_vals, $position;
# format the listbox-line
				formline($stat_format, @data_line);
# save the formatted string in the listbox
				$stat_entries{$position}=$^A;
		}

# save the value-array for the listbox
	@stat_vals = @sorted_vals;

	# update the label
	my @l_time = localtime;
	$scr{$STAT}->getobj('stat_label')->text(
		sprintf("Current data from scrutinizer - last update: %02d:%02d:%02d".
				" ($line_counter entries)", @l_time[2,1,0])
    );

	# update the screen
	$scr{$cur_scr}->focus();
}

sub black_timer_callback
{
	# check if scrutinizer.pl is still running
	# if not, terminate otherwise kill will block
	check_pid();

	# send the update signal
	kill USR1 => $scrutinizer_pid;

	# open the cmd-pipe
	if ( !(open(CMD, "> $pipe_cmd")) ) {
		serious_error("Couldn't open $pipe_cmd");
	}

	# write the request to the cmd-pipe
	print CMD "bl\n";

	# close the cmd-pipe
	close(CMD);

	my @bl_tab;
	my @bl_vals;
	my $line_counter = 0;
	my $position;
	my @data_line;

	# open the pipe
	if ( !(open(DATA, "< $pipe_data")) ) {
		serious_error("Couldn't read $pipe_data");
	}

	# read the table from the pipe
	while (<DATA>)
	{
		# create a 2D array from the string in one line !
		push @bl_tab, [split];
		# increment line counter
		$line_counter++;
	}

	# close the pipe
	close(DATA);

	# define $line variable
	my $line;

	# format each line and save it
	# into the temporary hash
	foreach $line (@bl_tab)
	{
		# clear the accumulator
		$^A="";
		# read the dataline
		@data_line = @{$line};
		# calc the remaining time
		#$data_line[2] = $data_line[3] - $data_line[2];
		# reset last column
		#$data_line[3] = 0;
		# save the position
		$position = shift(@data_line);
		# copy position into value-array of listbox
		push @bl_vals, $position;
		# format the listbox-line
		formline($black_format, @data_line);
		# save the formatted string in the listbox
		$black_entries{$position}=$^A;
	}

	# save the value-array for the listbox
	@black_vals = @bl_vals;

	# update the label
	my @l_time = localtime;
	$scr{$BLACK}->getobj('black_label')->text(
		sprintf("Current blacklisted IP's - last update: %02d:%02d:%02d".
				" ($line_counter entries)", @l_time[2,1,0])
    );

	# update the screen
	$scr{$cur_scr}->focus();

}

sub check_pid
{
	# check if pid file is accessible
	if (-r $pid_file) {
		# if true - do nothing
		return 1;
	} else {
		# disable timer, otherwise the dialog will be
        # sent to background after $scr{$STAT||$BLACK}->focus()
        $cui->disable_timer('data_timer') if ($cur_scr == $STAT);
		$cui->disable_timer('black_timer') if ($cur_scr == $BLACK);

		# make error message
		serious_error("The scrutinizer has terminated!\n".
					"Couldn't find the pid file '$pid_file'.");
		# terminate
		exit(1);
	}	
}

sub sort_stats
{
	if($cur_scr == $STAT) {
		# which column has to be sorted
		$stat_sorted_col = shift;

		# exec timer code for update instantly
		data_timer_callback();

		# set status text
		status_text("sorted by: $sorting{$stat_sorted_col}");

		# update the screen
		$scr{$cur_scr}->focus();
	}
}

sub serious_error
{
	# make error message
	$cui->error(
		-message => shift, 
		-title   => "Serious Error",
		-buttons => ['ok'],
	);
}

sub ban_dlg
{
	if ($cur_scr == $BLACK) {
		# clear the textentries
		$ipaddr->text("");
		$dur->text("");
		$loc->clear_selection();
		# make the dialog visible
		$ban_dlg->focus();
	}
}

sub unban_question
{
	if ($cur_scr == $BLACK) {
		# disable timer, otherwise the dialog will be
		# sent to background after $scr{$BLACK}->focus()
		$cui->disable_timer('black_timer');

		return if (!defined($blacklist->get_active_value()));

		# get actual line of listbox
		my @unban_ip = split(/ +/, 
				$black_entries{$blacklist->get_active_value()});

		# ask user if he really wants to unban the ip
		my $return = $cui->dialog(
			-message => "Really unban $unban_ip[1] ?",
			-title => "Unban IP Address",
			-buttons => ['yes', 'no'],
		);

		# if yes then send the unban signal
		if ($return) {
			# unban the ip
			unban_ip($unban_ip[1]);
		}
	}
}

sub ban_ip
{
	# get the arguments
	my $ip = shift;
	my $duration = shift;
	my $location = shift;

	# check the arguments
	
	# the ip address
	if ($ip !~ /(([0-2]?\d{1,2}\.){3}[0-2]?\d{1,2})/) {
		serious_error("Illegal ip address.");
		return;
	}

	# the duration
	if ($duration !~ /^\d+$/) {
		serious_error("Illegal duration.");
		return;
	}

	# the location
	if ($location !~ /[01]/) {
		serious_error("Illegal level.");
		return;
	}

	# take focus away from ban dialog
	$ban_dlg->loose_focus();

	# prepare the location
	$location = ($location ? "LEVEL2" : "LEVEL1");
	
	# check if scrutinizer is running
	check_pid();

	# send signal
	kill USR1 => $scrutinizer_pid;

	# open the cmd-pipe
	if ( !(open(CMD, "> $pipe_cmd")) ) {
		serious_error("Couldn't open $pipe_cmd");
	}

	# write the request tot the cmd-pipe
	print CMD "ban $ip $duration $location\n";

	# close the cmd-pipe
	close(CMD);

	# check return value (ack|nak)

	# open the pipe
	if ( !(open(DATA, "< $pipe_data")) ) {
		serious_error("Couldn't read $pipe_data");
	}	

	# read the response
	while (<DATA>)
	{
		# read the respone value
		# inform the user if there's a problem
		chomp($_);
		if (lc($_) ne 'ack') {
			# disable timer, otherwise the dialog will be
	        # sent to background after $scr{$BLACK}->focus()
        	$cui->disable_timer('black_timer');

			$cui->dialog(
				-message => "Could not ban address '$ip' for ".
					"'$duration' seconds!\nPlease check your input.",
				-title => "Error occured",
				-buttons => ['ok'],
			);

			# enable timer again
			$cui->enable_timer('black_timer');
		}
	}

	# exec timer code for update instantly
	black_timer_callback();
}

sub unban_ip
{
	# get the argument
	my $ip = shift;

	# check if scrutinizer is running
	check_pid();

	# send signal
	kill USR1 => $scrutinizer_pid;

	# open the cmd-pipe
	if ( !(open(CMD, "> $pipe_cmd")) ) {
		serious_error("Couldn't open $pipe_cmd");
	}
	
	# write the request to the cmd-pipe
	print CMD "unban $ip\n";

	# close the cmd-pipe
	close(CMD);

	# check the return value (ack|nak)
	
	# open the pipe
	if ( !(open(DATA, "< $pipe_data")) ) {
		serious_error("Couldn't read $pipe_data");
	}

	# read the response
	while (<DATA>)
	{
		# read the response value
		# inform user if there's a problem
		chomp($_);
		if (lc($_) ne 'ack') {
			# disable timer, otherwise the dialog will be
	        # sent to background after $scr{$BLACK}->focus()
    	    $cui->disable_timer('black_timer') if ($cur_scr == $BLACK);

			$cui->dialog(
				-message => "Could not unban address '$ip'!",
				-title => "Error occured",
				-buttons => ['ok'],
			);

			# enable timer again
			$cui->enable_timer('black_timer');
		}

	}

	# exec timer code for update instantly
	black_timer_callback();

}

sub view_ip
{
	my $selected_ip;
	my @l_time;
	my @unsorted_tab;
	my @sorted_tab;
	my $line_counter;
	my $line;
	my @data_line;
	my $i = 1;
	my @sorted_vals;
	my $listbox = $scr{$STAT}->getobj('stats');

	# get the selected ip
	$stat_entries{$listbox->get()} =~ /^\s*(\S*)/;
	$selected_ip = $1;
	
	# get the time
	@l_time = localtime();
	
	# check if scrutinizer.pl is still running
	check_pid();
	
	# send the update signal
	kill USR1 => $scrutinizer_pid;
	
	# open the cmd-pipe
	if ( !(open(CMD,"> $pipe_cmd")) ) {
		serious_error("Couldn't open $pipe_cmd");
	}
	
	# write the request to the cmd-pipe
	print CMD "ip $selected_ip\n";
	
	# close the cmd-pipe
	close(CMD);

	# open the data-pipe
	if ( !(open(DATA, "< $pipe_data")) ) {
		serious_error("Couldn't open $pipe_data");
	}

	# read the table from the pipe
	while (<DATA>)
	{
		# create a 2D array from the string in one line!
		push @unsorted_tab, [split];
		# increment line counter
		$line_counter++;
	}

	# close the data-pipe
	close(DATA);

	# sort the table (1. by hits, 2. by time, 3. by uri)
	@sorted_tab = sort { 
		@{$b}[1] <=> @{$a}[1] 
		|| 
		@{$b}[3] <=> @{$a}[3] 
		||
		@{$a}[0] cmp @{$b}[0]
						} @unsorted_tab;

	my $t_time;

	# format each line and save it
	# into the temporary hash
	foreach $line (@sorted_tab)
	{
		# convert timestamp
		$t_time = @{$line}[3];
		$t_time = gmtime($t_time);
		@{$line}[3] = $t_time;
		# clear the accumulator
		$^A = "";
		# read the dataline
		@data_line = @{$line};
		# save the position in value array of listbox
		push @sorted_vals, $i;
		# format the listbox-line
		formline($ip_format, @data_line);
		# save the formatted text in the listbox
		$ip_entries{$i}=$^A;
		# increment value-counter
		$i++;
	}

	# save the value-array for the listbox
	@ip_vals = @sorted_vals;

	# update the label
	$scr{$IP}->getobj('ip_label')->text(
		sprintf("Current data from $selected_ip - last update: %02d:%02d:%02d",
					 @l_time[2,1,0])
	);

	# clear selection
	$listbox->clear_selection();

	# switch screen
	$cur_scr = $IP;

	# set status text
	status_text("sorted by: hits"); 

	# update the screen
	$scr{$cur_scr}->focus();	
}

##################################################
# bindings and focus
##################################################

# bind ctrl-q to exit function
$cui->set_binding(\&exit_question, "\cQ");

# bind ctrl-a to blacklist
$cui->set_binding(\&go_blacklist, "\cA");

# bind ctrl-s to statitics
$cui->set_binding(\&go_statistic, "\cS");

# bind x to back to menu
$cui->set_binding(\&back_to_menu, "x");

# bind a to sort alert
$cui->set_binding(sub{sort_stats(2) if ($cur_scr == $STAT)}, "a");

# bind s to sort short average
$cui->set_binding(sub{sort_stats(3) if ($cur_scr == $STAT)}, "s");

# bind l to sort long average
$cui->set_binding(sub{sort_stats(4) if ($cur_scr == $STAT)}, "l");

# bind p to sort period
$cui->set_binding(sub{sort_stats(5) if ($cur_scr == $STAT)}, "p");

# bind r to sort requests
$cui->set_binding(sub{sort_stats(6) if ($cur_scr == $STAT)}, "r");

# bind b to ban dialog
$cui->set_binding(\&ban_dlg, "b");

# bind u to unban
$cui->set_binding(\&unban_question, "u");

##################################################
# start up the hole thing
##################################################

# start at the statistic screen
$cur_scr = $STAT; 
$scr{$cur_scr}->focus();

# keep things rolling
$cui->mainloop();
