#!/usr/bin/perl 
##########################################################################
# $Id: sendmail,v 1.31 2003/11/26 14:38:42 kirk Exp $
##########################################################################

########################################################
# This was written and is maintained by:
#    Kenneth Porter <shiva@well.com>
#
# Please send all comments, suggestions, bug reports,
#    etc, to shiva@well.com.
########################################################

use Logwatch ':sort';

my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
my $Debug = $ENV{'LOGWATCH_DEBUG'} || 0;

#Local domains, used for the per-domain analysis.
my %LocalDomains;

$MsgsSent = 0;
$BytesTransferred = 0;
$HourReturns = 0;
$DaysReturns = 0;
$UserUnknown = 0;
$ReturnUserUnknown = 0;
$TLSAcceptFailed = 0;
$SaveMailPanic = 0;
$RemoteProtocolError = 0;
$ReturnReceipt = 0;

my %relay;
my %abuse;
my %largeHdrs;
my %notLocal;
my %MailRejected;

# Adds a new domain to the hash used for domain reporting.
sub InitDomainHash ($) {
   $Domain = $_[0];
   if ( ($Domain) = ($Domain =~ /^([\S]+)/) ) {
     $LocalDomains{$Domain}{"MsgsOut"} = 0;
     $LocalDomains{$Domain}{"MsgsIn"} = 0;
     $LocalDomains{$Domain}{"MsgsInternal"} = 0;
     $LocalDomains{$Domain}{"BytesOut"} = 0;
     $LocalDomains{$Domain}{"BytesIn"} = 0;
     $LocalDomains{$Domain}{"BytesInternal"} = 0;
   } # if
} # sub

if ($Detail >= 10) {
   # Reads the sendmail configuration files and builds the %LocalDomains
   # hash containing all local and relayed domains.
   my ($ThisLine,$ThisName);
   my ($LocalHostNames, $SendmailAccess);

   # Check for valid local-host-names file.
   if (defined($ENV{'sendmaillocalhostnames'})) {
      $LocalHostNames = $ENV{'sendmaillocalhostnames'};
   } else {
      $LocalHostNames = "/etc/mail/local-host-names";
   } # else
   if (-s $LocalHostNames) {
      # Read and process local-host-names
      open (READCONFFILE, $LocalHostNames) or die "Cannot open " . $LocalHostNames ."\n";
      while (defined($ThisLine = <READCONFFILE>)) {
         if ( ($ThisName) = ($ThisLine =~ /^([^#][^ ]+)/) ) {
            InitDomainHash($ThisName);
         } # if
      } # while
      close(READCONFFILE);
   } else {
     print "\nERROR: Could not open $LocalHostNames\n";
   } # if

   # Check for valid access map file.
   if (defined($ENV{'sendmailaccess'})) {
      $SendmailAccess = $ENV{'sendmailaccess'};
   } else {
      $SendmailAccess = "/etc/mail/access";
   } # if
   if (-s $SendmailAccess) {
      # Read and interpret the access map.
      open (READCONFFILE, $SendmailAccess) or die "Cannot open " . $SendmailAccess ."\n";
      while (defined($ThisLine = <READCONFFILE>)) {
         if ( ($ThisName) = ($ThisLine =~ /^([^#0-9][\S]+)[\s]+RELAY/) ) {
            InitDomainHash($ThisName);
         } # if
      } # while
      close(READCONFFILE);
   } else {
     print "\nERROR: Could not open $SendmailAccess\n";
   } # if

   # Initialise the Size distribution array
   my %SizeDist;
   @SizeNames = ('0 - 10k', '10k - 20k', '20k - 50k', '50k - 100k',
                 '100k - 500k', '500k - 1Mb', '1Mb - 2Mb', '2Mb - 5Mb',
                 '5Mb - 10Mb', '10Mb+');

   # Initialise the large messages hash.
   my %LargeMsgs;
} # if

# Unknown users with bounces <= $UnknownUserThreshold will only be
# printed if the detail level is >= 10. Setting this value to 0 disables
# it.
my $UnknownUsersThreshold = 0;

while (defined($ThisLine = <STDIN>)) {
	($QueueID) = ($ThisLine =~ m/^([a-zA-Z0-9]+): / );
	$ThisLine =~ s/^[a-zA-Z0-9]+: //;
	if (  ( $ThisLine =~ m/^alias database [^ ]* (auto)?rebuilt by/ ) or
		( $ThisLine =~ m/[0-9]* aliases, longest [0-9]* bytes, [0-9]* bytes total/ ) or 
		( $ThisLine =~ m/^starting daemon (.*):/ ) or 
		( $ThisLine =~ m/premature EOM/ ) or 
		( $ThisLine =~ m/unexpected close on connection from/ ) or 
		( $ThisLine =~ m/timeout waiting for input from/ ) or 
		( $ThisLine =~ m/lost input channel from/ ) or 
		( $ThisLine =~ m/DSN: Cannot send message for \d+ day/ ) or 
		( $ThisLine =~ m/: Service unavailable$/) or 
		( $ThisLine =~ m/Broken pipe|Connection (reset|timed out)/ ) or
		( $ThisLine =~ m/X-Spam/ ) or
		( $ThisLine =~ m/Milter message: body replaced/ ) or
		( $ThisLine =~ m/Milter: data/ ) or
		( $ThisLine =~ m/Milter change: header/ ) or
		( $ThisLine =~ m/Milter delete: header/ ) or
		( $ThisLine =~ m/Milter add: header: X-Virus-Scanned: by amavis/ ) or
		( $ThisLine =~ m/AUTH=server, relay=/ ) or
		( $ThisLine =~ m/discarded/ ) or
		( $ThisLine =~ m/headers too large/ ) or
		( $ThisLine =~ m/^clone [a-zA-Z0-9]+, owner=/ ) ) {
		# We don't care about these
	} elsif ( ($FromUser, $FromDomain, $Bytes, $NumRcpts, $RelayHost) = ($ThisLine =~ /^from=[\<]?([^@]+)[@]?([^\> ]+).*size=([0-9]+).*nrcpts=([0-9]+).*relay=(\[[0-9\.]+\]|[^ ]* \[[0-9\.]+\]|[^ ]+).*$/) ) {
		if ($NumRcpts > 0) {
			$MsgsSent++;
			$TotalRcpts += $NumRcpts;
			$BytesTransferred += $Bytes;
			$MailBomber{$RelayHost} += $NumRcpts;
			$MailBomberConn{$RelayHost}++;
			
			if ($Bytes <= 10240) {
				$SizeDist[0]{'Num'}++;
				$SizeDist[0]{'Bytes'} += $Bytes;
			} elsif ($Bytes <= 20480) {
				$SizeDist[1]{'Num'}++;
				$SizeDist[1]{'Bytes'} += $Bytes;
			} elsif ($Bytes <= 51200) {
				$SizeDist[2]{'Num'}++;
				$SizeDist[2]{'Bytes'} += $Bytes;
			} elsif ($Bytes <= 102400) {
				$SizeDist[3]{'Num'}++;
				$SizeDist[3]{'Bytes'} += $Bytes;
			} elsif ($Bytes <= 512000) {
				$SizeDist[4]{'Num'}++;
				$SizeDist[4]{'Bytes'} += $Bytes;
			} elsif ($Bytes <= 1048576) {
				$SizeDist[5]{'Num'}++;
				$SizeDist[5]{'Bytes'} += $Bytes;
			} elsif ($Bytes <= 2097152) {
				$SizeDist[6]{'Num'}++;
				$SizeDist[6]{'Bytes'} += $Bytes;
			} elsif ($Bytes <= 5242880) {
				$SizeDist[7]{'Num'}++;
				$SizeDist[7]{'Bytes'} += $Bytes;
			} elsif ($Bytes <= 10485760) {
				$SizeDist[8]{'Num'}++;
				$SizeDist[8]{'Bytes'} += $Bytes;
			} else {
				$SizeDist[9]{'Num'}++;
				$SizeDist[9]{'Bytes'} += $Bytes;
			}
		}

      # Add The message to a hash for later per-domain analysis.
      $Msgs{$QueueID}{"Relay"} = $RelayHost;
      if (($Detail >= 10)) {
        $Msgs{$QueueID}{"FromDomain"} = $FromDomain;
        $Msgs{$QueueID}{"FromUser"} = $FromUser;
        $Msgs{$QueueID}{"Size"} = $Bytes;
        $Msgs{$QueueID}{"Internal"} = 0;
        $Msgs{$QueueID}{"Outgoing"} = 0;
        $Msgs{$QueueID}{"Incomming"} = 0;
      } # if

   } elsif ( ($ToUser, $ToDomain) = ($ThisLine =~ m/^to=[\<]?([^@]*)[@]?([^,\>]+).*stat=/ ) ) {
      #Determine whether the message is local, inbound or outbound and
      #update the domains hash appropriately.
      if (($Detail >= 10)) {
        $FromDomain = $Msgs{$QueueID}{"FromDomain"};
        if (defined($LocalDomains{$FromDomain})) {
           if (defined($LocalDomains{$ToDomain})) {
              if ($Msgs{$QueueID}{"Internal"} == 0) {
                 $Msgs{$QueueID}{"Internal"} = 1;
                 $LocalDomains{$FromDomain}{"MsgsInternal"}++;
                 $LocalDomains{$FromDomain}{"BytesInternal"} += $Msgs{$QueueID}{"Size"};
              } # if
           } else {
              if ($Msgs{$QueueID}{"Outgoing"} == 0) {
                 $Msgs{$QueueID}{"Outgoing"} = 1;
                 $LocalDomains{$FromDomain}{"MsgsOut"}++;
                 $LocalDomains{$FromDomain}{"BytesOut"} += $Msgs{$QueueID}{"Size"};
              } # if
           } # else
        } else {
           if (defined($LocalDomains{$ToDomain})) {
              if ($Msgs{$QueueID}{"Incomming"} == 0) {
                 $Msgs{$QueueID}{"Incomming"} = 1;
                 $LocalDomains{$ToDomain}{"MsgsIn"}++;
                 $LocalDomains{$ToDomain}{"BytesIn"} += $Msgs{$QueueID}{"Size"};
              } # if
           } # if
        } # else

        if ($Msgs{$QueueID}{"Size"} > 5242880) {  #10485760
           $LargeMsgs{$Msgs{$QueueID}{"FromUser"} . "@" . $FromDomain . " \-\> " .$ToUser . "@" .$ToDomain}++;
        } # if
      } # if

   } elsif ( $ThisLine =~ m/X-Scanned-By: MIMEDefang/) {
      $Defang++;
   } elsif (($Size) = ($ThisLine =~ m/message size \(([0-9]+)\) exceeds maximum/)) {
      $OverSize++;
      $OverSizeBytes += $Size;
   } elsif ( ($User) = ($ThisLine =~ /^<([^ ]*)>... (User unknown|No such user)$/) ) {
      $UnknownUsers{lc $User}{$QueueID}++;
   } elsif ( ($Host) = ($ThisLine =~ /\(Name server: ([^ ]+): host not found\)/)) {
      $UnknownHosts{$Host}++;
   } elsif ( ($Domain) = ($ThisLine =~ /Domain of sender address ([^ ]+) does not/)) {
      $UnresolvedDomains{$Domain}++;
   } elsif ($ThisLine =~ /reject=550 5\.7\.1 <[^ ]*@([^ ]*)>\.\.\. Relaying Denied/) {
      # We block some particularly annoying spam domains with the
      # following in /etc/mail/access...
      # From:worduphosting.com	ERROR:550 5.7.1 Relaying Denied (Spammer)
      $KnownSpammer{$1}++;
   } elsif ( ($User) = ($ThisLine =~ /^ruleset=check_rcpt, arg1=<([^ ]*)>, relay=[^,]*, reject=550\s*[\d.]*\s*<[^ ]*>\.\.\. Mailbox disabled for this recipient/) ) {
      $DisabledMailbox{$User}{$QueueID}++;
   # test for unknown relay users (users we would have relayed elsewhere)
   } elsif ( ($User) = ($ThisLine =~ /^[a-zA-Z0-9]+: ruleset=check_rcpt.*<(.*?)>.*... User unknown$/) ) {
       $UnknownUserscheckrcpt{$User}++;
   } elsif ( ($Dest,$Relay) = ($ThisLine =~ /^ruleset=check_rcpt, arg1=<([^ ]*)>, relay=([^,]*), reject=550\s*[\d.]*\s*<[^ ]*>\.\.\. Relaying denied/) ) {
      $Temp = "From " . $Relay . " to " . $Dest;
      $RelayDenied{$Temp}++;
   } elsif ($ThisLine =~ /^ruleset=check_relay, arg1=[^,]*, arg2=[^,]*, relay=([^,]*), reject=550\s*[\d.]*\s*(Mail from|Rejected:) [^ ]* (refused by blackhole site|listed at) (.*)/) {
      $Temp = "From " . $1 . " by " . $4;
      $BlackHoled{$Temp}++;
   } elsif ( ($Relay,$BlSite) = ($ThisLine =~ /^ruleset=check_relay, arg1=[^,]*, arg2=[^,]*, relay=([^,]*), reject=553\s*[\d.]*\s*.*http:\/\/([^\/]*)\//) ) {
      $Temp = "From " . $Relay . " by " . $BlSite;
      $BlackHoled{$Temp}++;
      $BlackHoles{$BlSite}++;
   } elsif ( ($Relay,$BlSite) = ($ThisLine =~ /reject=553\s*[\d.]*\s*<[^ ]*>\.\.\. +Mail from ([\d\.]+) rejected\;see http:\/\/([^\/]*)\//) ) {
      #This is the another blackhole tag -mgt
      $Temp = "From " . $Relay . " by " . $BlSite;
      $BlackHoled{$Temp}++;
      $BlackHoles{$BlSite}++;
   } elsif ( ($BlSite, $Relay) = ($ThisLine =~ /reject=553\s*[\d.]*\s*<[^ ]*>\.\.\. +Email blocked using ORDB.org - see \<http:\/\/(ORDB\.org)\/lookup\/\?host\=([\d\.]+)/) ) {
      #This is the tag from ORDB site -mgt
      $Temp = "From " . $Relay . " by " . $BlSite;
      $BlackHoled{$Temp}++;
      $BlackHoles{$BlSite}++;
   } elsif ( ($Relay,$BlSite) = ($ThisLine =~ /^ruleset=check_rcpt, arg1=[^,]*, relay=([^,]*), reject=550\s*[\d.]*\s*<[^ ]*>\.\.\. Mail from [^ ]* refused by blackhole site ([^ ]*)/) ) {
      $Temp = "From " . $Relay . " by " . $BlSite;
      $BlackHoled{$Temp}++;
      $BlackHoles{$BlSite}++;
   } elsif ( ($User) = ($ThisLine =~ /^ruleset=check_mail, arg1=<([^ ]*)>, relay=[^,]*, reject=451\s*[\d.]*\s*Domain of sender address [^ ]* does not resolve/) ) {
      $DomainErrors{$User . ": (does not resolve)"}++;
   } elsif ( ($User) = ($ThisLine =~ /^ruleset=check_mail, arg1=<([^ ]*)>, relay=[^,]*, reject=553\s*[\d.]*\s*<[^ ]*>\.\.\. Domain of sender address [^ ]* does not exist/) ) {
      $DomainErrors{$User . " (does not exist)"}++;
   } elsif ( ($User) = ($ThisLine =~ /^ruleset=check_mail, arg1=<([^ ]*)>, relay=[^,]*, reject=553\s*[\d.]*\s*<[^ ]*>\.\.\. Domain name required for sender address .*/) ) {
      $DomainErrors{$User . " (missing)"}++;

   # test for all kinds of rejects due check_mail
   #h2G22Jq19062: ruleset=check_mail, arg1=<3popmeywsv@taylorinet.com>, relay=adsl-65-66-156-239.dsl.kscymo.swbell.net [65.66.156.239], reject=451 4.0.0 Domain must resolve. Contact us if you think this was a mistake.
   #h2GCaeq27382: ruleset=check_mail, arg1=<juno.com>, relay=[218.5.77.88], reject=553 5.5.4 <juno.com>... Domain name required for sender address juno.com
   #h2G9Iuq25136: ruleset=check_mail, arg1=<whalenqq_v__@gotoworld.com>, relay=172071.telemar.net.br [200.165.172.71] (may be forged), reject=451 4.0.0 Domain mustresolve. Contact us if you think this was a mistake.
   } elsif( ($arg,$relay,$reason) = ($ThisLine =~ /^ruleset=check_mail, arg1=<(.*?)>, relay=.*?\[(.*?)\].*?, reject=(.*)/) ) {
      $Temp = "[$relay] $arg\n\t$reason";
      $CheckMailReject{$Temp}++;

   #h2GGj4q30085: ruleset=check_rcpt, arg1=<zengcheng2@163.net>, relay=[218.25.142.7], reject=450 4.7.1 <zengcheng2@163.net>... Relaying temporarily denied. Cannotresolve PTR record for 218.25.142.7
   } elsif( ($arg,$relay,$reason) = ($ThisLine =~ /^ruleset=check_rcpt, arg1=<(.*?)>, relay=.*?\[(.*?)\], reject=(.*)/) ) {
       $Temp = "[$relay] $arg\n\t$reason";
       $CheckRcptReject{$Temp}++;

   #h2G4jUx22325: lost input channel from localhost [127.0.0.1] to MTA after rcpt
   } elsif ( ($Temp)  = ($ThisLine =~ /^(lost input channel from .*? to MTA after .*)/) ) {
       $LostInputChannel{$Temp}++;

   #h2G2FUx19181: timeout waiting for input from mail.bpsmailer.com. during client greeting
   } elsif ( ($Temp)  = ($ThisLine =~ /^(timeout waiting for input from .*? during .*)/) ) {
       $TimeoutWaiting{$Temp}++;

   #NOQUEUE: [66.200.95.123] did not issue MAIL/EXPN/VRFY/ETRN during connection to MTA
   #NOQUEUE: SMTP1.ADMANMAIL.COM [209.216.124.212] (may be forged) did not issue MAIL/EXPN/VRFY/ETRN during connection to MTA
   } elsif ( ($Temp)  = ($ThisLine =~ /^.*\[(.*?)\] .*did not issue .*? during connection to MTA/) ) {
       $DummyConnection{$Temp}++;

   #hA29V0hK013676: hnexfe06.hetnet.nl [195.121.6.172]: Possible SMTP
   #RCPT flood, throttling.
   } elsif ( ($Temp)  = ($ThisLine =~ /^.*\[(.*?)\]: Possible SMTP RCPT flood, throttling./) ) {
       $BadRcptThrottle{$Temp}++;

   #h2GKtU001122: DSN: Too many hops 26 (25 max): from <MAILER-DAEMON@atbusiness.com> via localhost, to <vlagrarycf@pacific.net.in>
   #h2GHtSx30926: SYSERR(root): Too many hops 26 (25 max): from <MAILER-DAEMON@atbusiness.com> via localhost, to <superstore@bpsmailer.com>
   } elsif ( ($Temp)  = ($ThisLine =~ /^.*?Too many hops (.*)/) ) {
       $TooManyHops{$Temp}++;
   } elsif ( ($Warning)  = ($ThisLine =~ /Authentication-Warning: [^ ]+: ([^ ]+ set sender to ( |)[^ ]+ using -f|.+ didn\'t use HELO protocol|[^ ]+ owned process doing -bs)/) ) {
      $AuthWarns{$Warning}++;
   } elsif ( ($Forward,$Error) = ($ThisLine =~ /^forward ([^ ]*): transient error: (.*)$/) ) {
      $Temp = $Forward . ": " . $Error;
      $ForwardErrors{$Temp}++;
   } elsif ( ($Forward,$Error) = ($ThisLine =~ /^forward ([^ ]*): (.*)/) ) {
      $Temp = $Forward . ": " . $Error;
      $ForwardErrors{$Temp}++;
   } elsif ( $ThisLine =~ m/(return to sender|sender notify): Warning: could not send message for past (\d) hours/ ) {
      $NumHours = $2;
      $HourReturns++;
   } elsif ( $ThisLine =~ m/(return to sender|sender notify): Cannot send message for (\d) days/ ) {
      $NumDays = $2;
      $DaysReturns++;
   } elsif ( ( $NOQUEUE ) = ($ThisLine =~ /\[([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\] (\(may be forged\) |)did not issue MAIL\/EXPN\/VRFY\/ETRN during connection to (MTA|Daemon0)/) ) {
      $Noqueuers{$NOQUEUE}++;
   } elsif ($ThisLine=~ /relay=(\S+)*.*\[(\d+.\d+.\d+.\d+)\], reject=444 4.4.4 \<([^\>]+)\>... Sorry (\S*)/) {
      chomp($host=$2." ". (defined($1) ? "(".$1.")" : "(unresolved)") );
      chomp($luser=$3);
      chomp($ruser=$4);
      $ruser="none" if (length($ruser)==0);
      $relay{$host}{$ruser}{$luser}++;
   } elsif ($ThisLine=~ /arg1=\<([^\>]+)\>, relay=(\S+)*.*\[([^\]]+)\], reject=444 4.4.4 Sorry (\S*)/) {
      chomp($host=$3." ". (defined($2) ? "(".$2.")" : "(unresolved)") );
      chomp($ruser=$1);
      $luser="none";
      $relay{$host}{$ruser}{$luser}++;
   } elsif ($ThisLine=~ /relay=(\S+)*.*\[(\d+.\d+.\d+.\d+)\], reject=441 4.4.1 \<([^\>]+)\>/) {
      chomp($host=$2." ". (defined($1) ? "(".$1.")" : "(unresolved)") );
      chomp($luser=$3);
      $notLocal{$host}{$luser}++;
   } elsif ($ThisLine=~ /headers too large .* from \[([^\]]+)/) {
      $largeHdrs{$1}++;
   } elsif ($ThisLine=~ /(\S+) \[([0-9\.]+)]: VRFY (\S+) \[rejected\]/) {
      chomp($host=$2." ". (defined($1) ? "(".$1.")" : "(unresolved)") );
      $luser=$3;
      $abuse{$host}{$luser}++;
   } elsif ( $ThisLine =~ m/(postmaster notify|return to sender): User unknown/ ) {
      $UserUnknown++;
   } elsif ( $ThisLine =~ m/timeout waiting for input from (\S+)/ ) {
      $Timeouts{$1}++;
   } elsif ( $ThisLine =~ m/timeout writing message to (\S+?)\.?:/ ) {
      $Timeouts{$1}++;
   } elsif ( $ThisLine =~ /\[([0-9\.]+)]: ETRN (\S+)/ ) {
      chomp($ETRN=$2." from ".$1);
      $ETRNs{$ETRN}++;
   } elsif ( $ThisLine =~ /rejecting connections on daemon MTA: load average: ([0-9]+)/ ) {
      $LoadAvg{$1}++;
   } elsif ($ThisLine=~ /reject=.*MESSAGE NOT ACCEPTED - (.+)/) {
      chomp($host=$1);
      $MailRejected{$host}++;
   } elsif ($ThisLine=~ /DSN: User unknown/) {
      $UserUnknown++;
   } elsif ($ThisLine=~ /return to sender: User unknown/) {
      $ReturnUserUnknown++;
   } elsif ($ThisLine=~ /TLS: error: accept failed/) {
      $TLSAcceptFailed++;
   } elsif ($ThisLine=~ /savemail panic/) {
      $SaveMailPanic++;
   } elsif ($ThisLine=~ /DSN: Return receipt/) {
      $ReturnReceipt++;
   } elsif ($ThisLine=~ /Remote protocol error/) {
      $RemoteProtocolError++;
  } elsif ( ($Host,$Attack) = ($ThisLine =~ /POSSIBLE ATTACK from ([^ ]+): ([^ ]+)$/) ) {
	  $AttackAttempt{$Host}{$Attack}++;
  } else {
	  $ThisLine =~ s/.*\: (DSN\: .*)/$1/;
	  $ThisLine =~ s/.*\: (postmaster notify\: .*)/$1/;
	  chomp($ThisLine);
	  # Report any unmatched entries...
	  $OtherList{$ThisLine}++; 
  }
}

if ($MsgsSent > 0) {
   print "\n\nBytes Transferred: $BytesTransferred\n";
   print "Messages Sent:     $MsgsSent\n";
   print "Total recipients:  $TotalRcpts";
}

if ($Defang > 0) {
   print "\n" . $Defang . " messages scanned by MIMEDefang";
}

if ($OverSize > 0) {
   print "\n\nRejected $OverSizeBytes bytes in $OverSize message(s)";
}

if ($HourReturns > 0) {
   print "\n\n" . $HourReturns . " messages returned after " . $NumHours . " hours";
}

if ($DaysReturns > 0) {
   print "\n\n" . $DaysReturns . " messages returned after " . $NumDays . " days";
}

if($TLSAcceptFailed > 0) {
   print "\n\n" . $TLSAcceptFailed . " TLS Accept Fail(s)";
}

if($UserUnknown > 0) {
   print "\n\n" . $UserUnknown . " DSN User Unknown notifications";
}

if($ReturnUserUnknown > 0) {
   print "\n\n" . $ReturnUserUnknown . " Returned messages due to unknown user";
}

if($SaveMailPanic > 0) {
   print "\n\n" . $SaveMailPanic . " Save Mail Panic's";
}

if($RemoteProtocolError > 0) {
   print "\n\n" . $RemoteProtocolError . " Remote Protocol Errors's";
}

if($ReturnReceipt > 0) {
	print "\n\n" . $ReturnReceipt . " Return Receipt's";
}

if (keys %AttackAttempt) {
	print "\nWARNING!!!!\n";
	print "Possible Attack:\n";
	foreach $Host (sort {$a cmp $b} keys %AttackAttempt) {
		print "   Attempt from $Host with:\n";
		foreach $Attack (sort {$a cmp $b} keys %{$AttackAttempt{$Host}}) {
			print "      $Attack : $AttackAttempt{$Host}{$Attack} Time(s)\n";
		}
	}
}

if (($Detail >= 10) and (keys %LocalDomains)) {
   print "\n\nMessage traffic by domain:\n";
   print "                         |   Inbound   |  Outbound   |  Internal   |    Total\n";
   print "Domain                   | Msgs Kbytes | Msgs Kbytes | Msgs Kbytes | Msgs Kbytes\n";
   print "-------------------------+-------------+-------------+-------------+------------\n";
   foreach $ThisOne (sort keys %LocalDomains) {
      if (($LocalDomains{$ThisOne}{"BytesIn"} + $LocalDomains{$ThisOne}{"BytesOut"} + $LocalDomains{$ThisOne}{"BytesInternal"}) > 0) {
         $LineMsgs = $LocalDomains{$ThisOne}{"MsgsIn"} + $LocalDomains{$ThisOne}{"MsgsOut"} + $LocalDomains{$ThisOne}{"MsgsInternal"};
         $LineBytes = $LocalDomains{$ThisOne}{"BytesIn"} + $LocalDomains{$ThisOne}{"BytesOut"} + $LocalDomains{$ThisOne}{"BytesInternal"};
         printf("%-25s|%5d %6d |%5d %6d |%5d %6d |%5d %6d\n", $ThisOne, $LocalDomains{$ThisOne}{"MsgsIn"}, $LocalDomains{$ThisOne}{"BytesIn"}/1024, $LocalDomains{$ThisOne}{"MsgsOut"}, $LocalDomains{$ThisOne}{"BytesOut"}/1024, $LocalDomains{$ThisOne}{"MsgsInternal"}, $LocalDomains{$ThisOne}{"BytesInternal"}/1024, $LineMsgs, $LineBytes/1024);

         $TotalMsgsIn += $LocalDomains{$ThisOne}{"MsgsIn"};
         $TotalMsgsOut += $LocalDomains{$ThisOne}{"MsgsOut"};
         $TotalMsgsInternal += $LocalDomains{$ThisOne}{"MsgsInternal"};
         $TotalBytesIn += $LocalDomains{$ThisOne}{"BytesIn"};
         $TotalBytesOut += $LocalDomains{$ThisOne}{"BytesOut"};
         $TotalBytesInternal += $LocalDomains{$ThisOne}{"BytesInternal"};
      } # if
   } # foreach
   print "-------------------------+-------------+-------------+-------------+------------\n";
   $LineMsgs = $TotalMsgsIn + $TotalMsgsOut + $TotalMsgsInternal;
   $LineBytes = $TotalBytesIn + $TotalBytesOut + $TotalBytesInternal;
   printf("TOTAL                    |%5d %6d |%5d %6d |%5d %6d |%5d %6d\n", $TotalMsgsIn, $TotalBytesIn/1024, $TotalMsgsOut, $TotalBytesOut/1024, $TotalMsgsInternal, $TotalBytesInternal/1024, $LineMsgs, $LineBytes/1024);
} # if

if (($Detail >= 10)) {
   print "\n\nMessage Size Distribution:\n";
   print "Range          # Msgs       KBytes\n";
   foreach $ThisOne (0..9) {
      printf("%-12s   %6d   %10d\n", $SizeNames[$ThisOne], $SizeDist[$ThisOne]{'Num'}, $SizeDist[$ThisOne]{'Bytes'}/1024);
      $TotalNum += $SizeDist[$ThisOne]{'Num'};
      $TotalBytes += $SizeDist[$ThisOne]{'Bytes'};
   }
   print  "----------------------------------\n";
   printf("TOTAL          %6d   %10d\n", $TotalNum, $TotalBytes/1024);
   if ($TotalNum > 0) {
      printf("Avg. Size               %10d\n", ($TotalBytes / $TotalNum)/1024);
   }
}

if (keys %LargeMsgs) {
   print "\n\nLarge Msaages (From \-\> To):\n";
   foreach $ThisOne (sort keys %LargeMsgs) {
      print "    $ThisOne: ${LargeMsgs{$ThisOne}} Times(s)\n";
   }
}

if (keys %ETRNs) {
   print "\n\nETRNs Received:\n";
   foreach $ThisOne (sort keys %ETRNs) {
      print "    " . $ThisOne . ": " . $ETRNs{$ThisOne} . " Times(s)\n";
   }
}

if (keys %LoadAvg) {
   print "\n\nConnections Rejected due to load average::\n";
   foreach $ThisOne (sort keys %LoadAvg) {
      print "    Load Avg " . $ThisOne . ": " . $LoadAvg{$ThisOne} . " Times(s)\n";
   }
}

if (keys %UnknownUsers) {
   foreach $Usr (sort keys %UnknownUsers) {
      foreach $QueueID (sort keys %{ $UnknownUsers{$Usr} }) {
         $SortedUsers{$Usr}{$Msgs{$QueueID}{"Relay"}}++;
      }
      @v = values %{$SortedUsers{$Usr}};
   }
   print "\n\nUnknown local users:\n";
   foreach $Usr (sort keys %SortedUsers) {
      unless ($Detail >= 10) {
        my $sum = 0;
        grep { $sum += $_ } values %{$SortedUsers{$Usr}};
      }
      if ($Detail >= 10 || $sum > $UnknownUsersThreshold) {
         print "\n    $Usr\n";
         my $sort = CountOrder( %{$SortedUsers{$Usr}} );
         foreach $RelayHost (sort $sort keys %{ $SortedUsers{$Usr} }) {
            print "      from $RelayHost    $SortedUsers{$Usr}{$RelayHost} time(s).\n";
         }
      }
   }
}

if (keys %UnknownUserscheckrcpt) {
   print "\n\nUnknown relay users: (check_rcpt)\n";
   foreach $ThisOne (keys %UnknownUserscheckrcpt) {
      print "    " . $ThisOne . ": " . $UnknownUserscheckrcpt{$ThisOne} . " Times(s)\n";
   }
}

if (keys %DisabledMailbox) {
   %SortedUsers = ();
   foreach $Usr (sort keys %DisabledMailbox) {
      foreach $QueueID (sort keys %{ $DisabledMailbox{$Usr} }) {
         $SortedUsers{$Usr}{$Relays{$QueueID}}++;
      }
   }
   print "\n\nDisabled mailboxes:\n";
   foreach $Usr (sort keys %SortedUsers) {
      print "\n    $Usr\n";
      foreach $RelayHost (sort keys %{ $SortedUsers{$Usr} }) {
         print "      from $RelayHost    $SortedUsers{$Usr}{$RelayHost} time(s).\n";
      }
   }
}

$count = 0;
foreach $ThisOne (sort {$MailBomber{$b}<=>$MailBomber{$a}} keys %MailBomber) {
   if ($MailBomber{$ThisOne} >= 10 and $count < 50) {
      print "\n\nTop relays (recipients/connections - min 10 rcpts, max 50 lines):\n" if ! $count;
      print "    $MailBomber{$ThisOne}/$MailBomberConn{$ThisOne}: $ThisOne\n";
   }
   $count++;
}

if (keys %KnownSpammer) {
   print "\n\nRelay attempts from known spammers:\n";
   foreach $ThisOne (sort keys %KnownSpammer) {
      print "    " . $ThisOne . ": " . $KnownSpammer{$ThisOne} . " Times(s)\n";
   }
}

if (keys %RelayDenied) {
   print "\n\nRelaying denied:\n";
   my $count = CountOrder(%RelayDenied);
   foreach $ThisOne (sort $count keys %RelayDenied) {
      print "    " . $ThisOne . ": " . $RelayDenied{$ThisOne} . " Times(s)\n";
   }
}

if (keys %CheckMailReject) {
   print "\n\nRejected incoming mail:\n";
   foreach $ThisOne (keys %CheckMailReject) {
      print "    " . $ThisOne . ": " . $CheckMailReject{$ThisOne} . " Times(s)\n";
   }
}

if (keys %CheckRcptReject) {
   print "\n\nRejected outgoing mail:\n";
   foreach $ThisOne (keys %CheckRcptReject) {
      print "    " . $ThisOne . ": " . $CheckRcptReject{$ThisOne} . " Times(s)\n";
   }
}

if (keys %LostInputChannel) {
   print "\n\nLost input channel:\n";
   foreach $ThisOne (keys %LostInputChannel) {
      print "    " . $ThisOne . ": " . $LostInputChannel{$ThisOne} . " Times(s)\n";
   }
}

if (keys %TimeoutWaiting) {
   print "\n\nTimeout waiting:\n";
   foreach $ThisOne (keys %TimeoutWaiting) {
      print "    " . $ThisOne . ": " . $TimeoutWaiting{$ThisOne} . " Times(s)\n";
   }
}

if (keys %DummyConnection) {
   print "\n\nClient quit before communicating:\n";
   foreach $ThisOne (sort keys %DummyConnection) {
      print "    " . $ThisOne . ": " . $DummyConnection{$ThisOne} . " Times(s)\n";
   }
}

if (keys %BadRcptThrottle) {
   print "\n\nClient submitted too many bad recipients:\n";
   foreach $ThisOne (sort keys %BadRcptThrottle) {
      print "    " . $ThisOne . ": " . $BadRcptThrottle{$ThisOne} . " Times(s)\n";
   }
}

if (keys %TooManyHops) {
   print "\n\nToo many hops:\n";
   foreach $ThisOne (sort keys %TooManyHops) {
      print "    " . $ThisOne . ": " . $TooManyHops{$ThisOne} . " Times(s)\n";
   }
}

if (keys %BlackHoled) {
   print "\n\nBlackHole Totals:\n";
   foreach $ThisOne (sort keys %BlackHoles) {
      print "    " . $ThisOne . ": " . $BlackHoles{$ThisOne} . " Times(s)\n";
   }
   print "\nBlackholed:\n";
   foreach $ThisOne (sort keys %BlackHoled) {
      print "    " . $ThisOne . ": " . $BlackHoled{$ThisOne} . " Times(s)\n";
   }
}

if (keys %DomainErrors) {
   print "\n\nUnresolveable or non-existent domains:\n";
   my $count = CountOrder(%DomainErrors);
   foreach $ThisOne (sort $count keys %DomainErrors) {
      print "    " . $ThisOne . ": " . $DomainErrors{$ThisOne} . " Times(s)\n";
   }
}

if (keys %AuthWarns) {
   print "\n\nAuthentication warnings:\n";
   foreach $ThisOne (sort keys %AuthWarns) {
      print "    " . $ThisOne . ": " . $AuthWarns{$ThisOne} . " Times(s)\n";
   }
}

if (keys %UnknownHosts) {
   print "\n\nUnknown hosts:\n";
   my $count = CountOrder(%UnknownHosts);
   foreach $ThisOne (sort $count keys %UnknownHosts) {
      print "    " . $ThisOne . ": " . $UnknownHosts{$ThisOne} . " Times(s)\n";
   }
}

if (keys %UnresolvedDomains) {
   print "\n\nUnresolved sender domains:\n";
   my $count = CountOrder(%UnresolvedDomains);
   foreach $ThisOne (sort $count keys %UnresolvedDomains) {
      print "    " . $ThisOne . ": " . $UnresolvedDomains{$ThisOne} . " Times(s)\n";
   }
}

if (keys %Timeouts) {
   print "\n\nTimeouts:\n";
   my $count = CountOrder(%Timeouts);
   foreach $ThisOne (sort $count keys %Timeouts) {
      print "    " . $ThisOne . ": " . $Timeouts{$ThisOne} . " Times(s)\n";
   }
}

if (keys %ForwardErrors) {
   print "\n\nForwarding errors:\n";
   my $count = CountOrder(%ForwardErrors);
   foreach $ThisOne (sort $count keys %ForwardErrors) {
      print "    " . $ThisOne . ": " . $ForwardErrors{$ThisOne} . " Times(s)\n";
   }
}

if (keys %Noqueuers) {
   print "\n\nDid not issue MAIL/EXPN/VRFY/ETRN during connection to MTA:\n";
   my $count = CountOrder(%Noqueuers, \&SortIP);
   foreach $ThisOne (sort $count keys %Noqueuers) {
      printf "    %-17s : %4i Time(s)\n" , $ThisOne , $Noqueuers{$ThisOne};
   }
}

if (keys %MailRejected) {
  print "\n\nMail was rejected because of the following entries in the access database:\n";
  foreach $ThisOne (sort keys %MailRejected) {
      printf "    %-50s : %3i Time(s)\n" , $ThisOne , $MailRejected{$ThisOne};
   }
}

if (keys %relay) {
   print "\n\nWe do not relay for these (host,ruser,luser):\n";
   foreach $host (sort keys %relay) {
      print "\n    $host\n";
      foreach $ruser (sort keys %{ $relay{$host} }) {
         print "      $ruser\n";
         foreach $luser (sort keys %{$relay{$host}{$ruser}}) {
            printf "            %-30s %i \n",$luser,$relay{$host}{$ruser}{$luser};
         }
      }
   }
}

if (keys %notLocal) {
   print "\n\nAddress not local from these (host, user): \n";
   foreach $host (sort keys %notLocal ) {
      print "\n    $host\n";
      foreach $luser (sort keys %{ $notLocal{$host} }) {
         printf "     %-30s %i \n",$luser,$notLocal{$host}{$luser};
      }
   }
}

if (keys %abuse) {
   my $total;
   print "\n\nrejected VRFY (host,ruser):\n";
   foreach $host (sort keys %abuse) {
      print "\n    $host\n";
      $total = 0;
      foreach $luser (sort keys %{$abuse{$host}}) {
         print "       $luser\n";
         $total+=$abuse{$host}{$luser};
      }
      print " Total per host:$total\n";
   }
}

if (keys %largeHdrs) {
   print "\n\nToo large headers from: \n";
   foreach $host ( sort {$largeHdrs{$b}<=>$largeHdrs{$a}} keys %largeHdrs ) {
      printf "    %-17s   %-3i Time(s)\n",$host, $largeHdrs{$host};
   }
}

if (keys %OtherList) {
   print "\n**Unmatched Entries**\n";
   foreach $line (sort {$OtherList{$b}<=>$OtherList{$a} } keys %OtherList) {
      print "   $line: $OtherList{$line} Time(s)\n";
   }
}

exit(0);

# vi: shiftwidth=4 tabstop=4
