#!/usr/local/bin/perl

# $Id: cnt-spam.pl,v 1.2 1997/10/06 21:04:15 tim Exp $

#  cnt-spam.pl - counts the number of rejected messages out of 
#	the mail log file.  not very good on counting true
#	message numbers.   only displays the top 10 problems.
#
# todo:  work on parsing up the headers to include relay info. 

# Tim Wicinski,  tim@barn.com

$compress = 'gzip -d -c ';

$file="/var/adm/mail.log";

$file = $ARGV[0] if ($ARGV[0]);
die "can not open $file\n" unless (-f $file);

if ($file =~ /\.gz$/ || $file =~ /\.Z$/) { 
	open (IN, "$compress $file |") || 
		die "can not open cmpressed file $file\n";
} else  {
	open (IN, "$file") || die "can not open $file\n";
}

while (<IN>) {
	chop;
	($a, $b, $c, $d, $e, $smid, $line) = split (/\s+/, $_, 7);

	# take the hope that this should contain a msgid
	unless ($smid =~ /:/) {
		$BadMsgId{$smid}++;
		next;
	}

	$smid =~ s/://;
	$MAIL{$smid} .= " $line"; # we keep all of them in case...

	next unless (/reject=/);

	s/^.*arg1=<//;
	s/ relay=(.*), reject=...//;

	($name, $n, $msg) = split(/\s/, $_, 3);

	$name =~ s/>,//;
	$name =~ tr/A-Z/a-z/;

	if ($msg =~ /This domain/) {
		$name =~ s/^(.*)@//;
		$domain{$name}++;
		$domains_banned++;
	} elsif ($msg =~ /You are/) {
		$person{$name}++;
		$people_banned++;
	} elsif ($msg =~ /unresolvable/) {
		$unresolved{$name}++;
		$unresolved_lines++;
	} elsif ($msg =~ /invalid host name/i) {
		$invalid{$name}++;
		$invalid_lines++;
	} elsif ($msg =~ /do not relay/) {
		($user, $dom) = split (/\@/, $name, 2); # cnt by domain 
		$relay{$dom}++;
		$relay_count++;
	}
}

close (IN);

$banned_lines = $domains_banned + $people_banned;

$total_lines = scalar (keys %MAIL);

print "Total messages\t$total_lines\n";
printf "Banned messages\t%d\t%.3f %\n", $banned_lines,$banned_lines/$total_lines * 100 if ($banned_lines);
printf "Unresolvables\t%d\t%.3f %\n", $unresolved_lines, $unresolved_lines/$total_lines * 100 if ($unresolved_lines);
printf "Invalid Hosts\t%d\t%.3f %\n", $invalid_lines, $invalid_lines/$total_lines * 100 if ($invalid_lines);
printf "Illegal Relay Attempts\t%d\t%.3f %\n", $relay_count, $relay_count/$total_lines * 100 if ($relay_count);

if ($domains_banned) {
	print "\nDomains Banned \n";
	&count_array (*domain);
}

if ($people_banned) {
	print "\nPeople Banned\n";
	&count_array (*person);
} 

if ($unresolved_lines) {
	print "\nUnresolvable addresses\n";
	&count_array (*unresolved);
}

if ($invalid_lines) {
	print "\nIllegal Host names\n";
	&count_array (*invalid);
}

if ($relay_count) {
	print "\nIllegal Relay Attempts (by name of from header)\n";
	&count_array (*relay);
}

if (scalar (%BadMsgId)) {
	print "\nMiscellaneous Sendmail Messages\n";
	foreach $key (keys %BadMsgId) {
		$bad_ids++;
		if ($key =~ /restart/i) {
			print "\tRestarted sendmail $BadMsgId{$key} times\n";
		} elsif ($key =~ /starting/i) {
			print "\tStarted sendmail $BadMsgId{$key} times.\n";
		} elsif ($key =~ /alias/i) {
			print "\tReloaded the aliases file $BadMsgId{$key} times.\n";
		} elsif ($key =~ /tcpwrapper/i) {
			print "\t$BadMsgId{$key} tcpwrapper rejections.\n";
		} elsif ($key =~ /message/i) {
			next; # this is "last message repeated n times"
		} else {
			print "\t$key occurred $BadMsgId{$key} times.\n";
		}
	}
}

# dump the msg breakdown for further debugging.

# open (OUT, ">/tmp/mail.output") || die;
# foreach $smid (sort keys %MAIL) {
# 	print OUT "$smid\t$MAIL{$smid}\n";
# }
# close (OUT);

exit;

#
# -------------------
# these take the associative arrays and attempts to sort them
# numerically by amount of times hit. It's done fairly lamely - takes
# each key from the associative and keeps that and it's total in lock
# step in another array.  this can be done better.
# 

sub count_array {
	local (*array) = @_;

	for ($i = 0; $i <= 9; $i++) {
		$TUN{$i} = 0;
		$TUA{$i} = "XXX";
	}

	foreach $key (sort (keys %array)) {
		for ($i = 0; $i <= 9; $i++) {
			if ($array{$key} > $TUN{$i}) {
#				print "picking $i $key $array{$key} over $TUN{$i} $TUA{$i}\n";
				&push_down_TUN ($i, $array{$key});
				&push_down_TUA ($i, $key);
				goto here; # no need to keep going
			}
		}
	here: 
	}

	for ($i = 0; $i <= 9; $i++) {
		print "$TUN{$i}\t$TUA{$i}\n" unless ($TUN{$i} == 0);
	}
}


sub push_down_TUA {
	local ($start, $value) = @_;

	local $j;

	for ($j = 8; $j >= $start; $j--) {
		$TUA{$j+1} = $TUA{$j};
	}

	$TUA{$start} = $value;
}

sub push_down_TUN {
	local ($start, $value) = @_;

	local $j;
	for ($j = 8; $j >= $start; $j--) {
		$TUN{$j+1} = $TUN{$j};
	}
	$TUN{$start} = $value;
}

