#!/usr/bin/perl -w

#   Copyright (C) 2004 Nigel Horne <njh@bandsman.co.uk>
# 
#   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.
# 
#   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.

#	clamavmon - monitor a network for virus intrusion
# Usage "clamavmon [machines...]" where machines is an optional list of machine
# running clamd. These will turn from green to red if clamd dies. Clicking on
# the machine name gives the version of clamd running there.
# FIXME: clicking should give the data base information and date of last
#	freshclam

eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}' if 0; 

use strict;
use IO::Socket::INET;
use Tk;
use Tk::Dialog;
use threads;	# Tk isn't thread safe, which really means it can't be used
		# in applications such as this which gather and display data
		# in real time. I would have prefered to use TCL, but that
		# doesn't come with UDP support, so I'd have to build that in
		# as extension.

my $mw = MainWindow->new;

# $mw->bind('<Alt-F4>' => \&quit);

my $top = $mw->Frame();
my $middle = $mw->Frame();
my $bottom = $mw->Frame();

my @machines;

if(@ARGV) {
	my $offset = 0;
	foreach(@ARGV) {
		$machines[$offset++] = $top->Button(-text => $_, -command => [ \&drill, $_ ])->pack(-side => 'left');
	}
}

$top->pack();

my $text = $middle->Scrolled('Text', -width => 50, -scrollbars => 'ow')->pack;
# $text->bind('<Alt-F4>' => \&quit);

$middle->pack();

my $b1 = $bottom->Button(-text => 'About', -command => \&about)->pack(-side => 'left');
# my $b2 = $bottom->Button(-text => 'Quit', -command => \&quit)->pack(-side => 'left');
my $b2 = $bottom->Button(-text => 'Quit', -command => \&exit)->pack(-side => 'left');

$bottom->pack();

my $t1 = threads->new(\&listener);

if(@ARGV) {
	my $t2 = threads->new(\&pinger);
}

# $t->detach();

my $quitting = 0;

my $history = "";

MainLoop;

sub listener {
	until($quitting) {
		my $sock = IO::Socket::INET->new(
			LocalPort => 3310,
			Proto => 'udp',
			Type => SOCK_DGRAM) or die "$0: socket: $!\n";

		my $mess;
		$sock->recv($mess, 128);

		my ($rport, $ipaddr) = sockaddr_in($sock->peername);

		close $sock;

		$text->insert('end', "From " . inet_ntoa($ipaddr) . " $mess\n");

		# print "From " . inet_ntoa($ipaddr) . " $mess\n";
 
		# $text->Contents($history);
		# $text->pack;
	}
	print "quit\n";
}

sub pinger {
	until($quitting) {
		foreach(@machines) {
			my $machine = $_->cget('-text');
			my $sock = IO::Socket::INET->new(
				PeerPort => 3310,
				PeerAddr => $machine,
				Proto => 'tcp',
				Timeout => 10,
				Type => SOCK_STREAM);

			my $mess = "";

			my $background = $_->cget('-background');

			if($sock) {
				print $sock "PING\n";

				$mess = <$sock>;

				close $sock;
			}

			if($mess ne "PONG\n") {
				# print "$machine is down\n";
				if($background ne 'red') {
					$_->configure(-background => 'red');
				}
			} else {
				# print "$machine is up\n";
				if($background ne 'green') {
					$_->configure(-background => 'green');
				}
			}
		}
		select(undef, undef, undef, 60);
	}
}

sub quit {
	$quitting = 1;
	# $t->join();
	exit;
}

# TODO: this should be modeless
sub about {
	my $about = $mw->DialogBox(
		-title=>"About clamAVmon",
		-buttons=>["OK"]
	);

	$about->add('Label',
		-anchor => 'w',
		-justify => 'left',
		-text => "clamAVmon\n" .
			"Copyright (C) 2004 Nigel Horne njh\@bandsman.co.uk\n" .
			"The GPL Licence will appear here")->pack;

	$about->Show();
}

sub drill {
	my $machine = shift;

	my $sock = IO::Socket::INET->new(
		PeerPort => 3310,
		PeerAddr => $machine,
		Proto => 'tcp',
		Timeout => 10,
		Type => SOCK_STREAM) or die "$0: socket: $!\n";

	print $sock "VERSION\n";

	my $mess = <$sock>;

	close $sock;

	print $mess;

	my $state = $mw->DialogBox(
		-title=>$machine,
		-buttons=>["OK"]
	);

	$state->add('Label',
		-anchor => 'w',
		-justify => 'left',
		-text => $mess)->pack;

	$state->Show();
}