#!/usr/bin/perl

use strict;
use lib qw(/usr/lib/libDrakX);
use common;

use Socket;
use mygtk2 qw(gtknew);
use POSIX qw(strftime);
use network::activefw;

use Gtk2::SimpleList;

use ugtk2 qw(:create :helpers :wrappers);

my $blacklist = Gtk2::SimpleList->new(addr => 'hidden',
                                      timestamp => 'hidden',
                                      N("Date") => 'text',
                                      N("Attacker") => 'text',
                                      N("Attack type") => 'text',
                                      N("Service") => 'text',
                                      N("Network interface") => 'text',
                                      N("Protocol") => 'text',
                                  );
$blacklist->get_selection->set_mode('multiple');
$blacklist->set_headers_clickable(1);
foreach (0, 1, 2) {
    $blacklist->get_column($_)->signal_connect('clicked', \&sort_by_column, $blacklist->get_model);
    #- sort on timestamp if Date column is clicked
    #- sort columns include hidden columns while list columns don't
    $blacklist->get_column($_)->set_sort_column_id($_ == 0 ? 1 : $_ + 2);
}

my $whitelist = Gtk2::SimpleList->new(addr => 'hidden',
                                      N("Attacker") => 'text',
                                  );
$whitelist->get_selection->set_mode('multiple');
$whitelist->set_headers_clickable(1);
$whitelist->get_column(0)->signal_connect('clicked', \&sort_by_column, $whitelist->get_model);
$whitelist->get_column(0)->set_sort_column_id(0);

my $activefw = activefw->new(sub {
    my ($con, $msg) = @_;
    handle_blacklist($msg->get_args_list) if
      $msg->get_interface eq "com.mandrakesoft.activefirewall" &&
      $msg->get_path eq "/com/mandrakesoft/activefirewall" &&
      $msg->get_member eq "Blacklist";

    handle_whitelist($msg->get_args_list) if
      $msg->get_interface eq "com.mandrakesoft.activefirewall" &&
      $msg->get_path eq "/com/mandrakesoft/activefirewall" &&
      $msg->get_member eq "Whitelist";

    clear_lists() if
      $msg->get_interface eq "com.mandrakesoft.activefirewall" &&
      $msg->get_path eq "/com/mandrakesoft/activefirewall" &&
      $msg->get_member eq "Clear";

    handle_init() if
      $msg->get_interface eq "com.mandrakesoft.activefirewall" &&
      $msg->get_path eq "/com/mandrakesoft/activefirewall" &&
      $msg->get_member eq "Init";
});
init_lists();

$ugtk2::wm_icon = "/usr/lib/libDrakX/icons/drakfirewall.png";
my $w = ugtk2->new(N("Active Firewall"));
gtkpack($w->{window},
        gtknew('Notebook', children => [
            gtknew('Label', text => N("Blacklist")),
            gtknew('VBox', spacing => 5, children => [
                1, gtknew('ScrolledWindow', width => 600, height => 400, child => $blacklist),
                0, gtknew('HButtonBox', layout => 'edge', children_loose => [
                    gtknew('Button', text => N("Remove from blacklist"), clicked => sub { unblacklist(get_selected_blacklist()) }),
                    gtknew('Button', text => N("Move to whitelist"), clicked => sub { whitelist(get_selected_blacklist()) }),
                    gtknew('Button', text => N("Quit"), clicked => sub { Gtk2->main_quit })
                ]),
            ]),
            gtknew('Label', text => N("Whitelist")),
            gtknew('VBox', spacing => 5, children => [
                1, gtknew('ScrolledWindow', width => 600, height => 400, child => $whitelist),
                0, gtknew('HButtonBox', layout => 'edge', children_loose => [
                    gtknew('Button', text => N("Remove from whitelist"), clicked => sub { unwhitelist(get_selected_whitelist()) }),
                    gtknew('Button', text => N("Quit"), clicked => sub { Gtk2->main_quit })
                ]),
            ]),
        ]),
);
$w->show;
Gtk2->main;

ugtk2::exit(0);

sub sort_by_column {
    my ($column, $model) = @_;
    my $col_id = $column->get_sort_column_id;
    my ($old_id, $old_order) = $model->get_sort_column_id;
    $model->set_sort_column_id($col_id, $old_id == $col_id && $old_order ne 'descending' ? 'ascending' : 'descending');
}

sub handle_init {
    $activefw->find_daemon;
    init_lists();
}

sub list_remove_addr {
    my ($list, @addr) = @_;
    #- workaround buggy Gtk2::SimpleList array abstraction, it corrupts references
    foreach (0 .. $#{$list}) {
        member($list->[$_][0], @addr) and splice @$list, $_, 1;
    }
}

sub init_blacklist {
    my @packets = $activefw->get_blacklist;
    while (my @blacklist = splice(@packets, 0, 8)) {
        handle_blacklist(@blacklist);
    }
}

sub clear_blacklist {
    @{$blacklist->{data}} = ();
}

sub handle_blacklist {
    my ($timestamp, $indev, $prefix, $sensor, $protocol, $addr, $port, $icmp_type) = @_;
    push @{$blacklist->{data}}, [
        $addr,
        $timestamp,
        activefw::format_date($timestamp),
        activefw::resolve_address(activefw::get_ip_address($addr)),
        $prefix eq 'SCAN' ? N("Port scanning") :
        $prefix eq 'SERV' ? N("Service attack") :
        $prefix eq 'PASS' ? N("Password cracking") :
        '',
        activefw::get_service($port) || '',
        $indev,
        $protocol || '',
    ];
}

sub get_selected_blacklist {
    uniq(map { $blacklist->{data}[$_][0] } $blacklist->get_selected_indices);
}

sub unblacklist {
    my @addr = @_;
    $activefw->unblacklist($_) foreach @addr;
    #- delete from the list even if the above calls were unsuccessful
    list_remove_addr($blacklist->{data}, @addr);
}

sub init_whitelist {
    handle_whitelist($_) foreach $activefw->get_whitelist;
}

sub clear_whitelist {
    @{$whitelist->{data}} = ();
}

sub handle_whitelist {
    my ($addr) = @_;
    push @{$whitelist->{data}}, [ $addr, activefw::resolve_address(activefw::get_ip_address($addr)) ];
}

sub get_selected_whitelist {
    uniq(map { $whitelist->{data}[$_][0] } $whitelist->get_selected_indices);
}

sub whitelist {
    my @addr = @_;
    unblacklist(@addr);
    $activefw->whitelist($_) foreach @addr;
}

sub unwhitelist {
    my @addr = @_;
    $activefw->unwhitelist($_) foreach @addr;
    #- delete from the list even if the above calls were unsuccessful
    list_remove_addr($whitelist->{data}, @addr);
}

sub init_lists {
    init_blacklist();
    init_whitelist();
}

sub clear_lists {
    clear_blacklist();
    clear_whitelist();
}