[Logwatch-Devel] sendmail script

Bjorn L. bl_logwatch at mblmail.net
Sat Feb 5 11:10:25 MST 2005


Mike,

Thanks for letting me know about the issues in my previous patch,
on the counts.

I stepped through the code of the sendmail script, line by line.  I
attempted to identify the source of every error message, and documented
it into the original sendmail file.  I also made the following changes:

- Changed the matching expression to mimic the execution of sendmail.
   It appears that currently many of the error messages were parsed based
   on specific examples, which doesn't catch the general case.

- Added many statements to allow for the logging at level 15, which
   catches many more errors.  (I know I don't have all of them, but at
   least the most common ones.)

- Counted some errors that were in the section intended for errors to be
   ignored.

- There were several overlapping matching statements.  For example,
   several types of errors were not caught because they matched in
   the first section of lines to ignore, but later had code to count
   these errors, which had already matched (and so is never executed).

- Added STARTTLS counts and delivery agent counts from before, as well
   as your new greet_pause code

I realize there is a lot of stuff there, so let me know if you'd
like me to break things down a bit, or explain any of the changes.
And of course, I do appreciate you (or anyone) running it against
their logs, as I don't have a good collection of them.

In the matching section, the comments that start in the first column
are my questions.

So all the changes above were really targeted at the matching sections,
not the reporting ones.  For that, I'd like to propose the following:

- Organize errors by type.  Perhaps something like:

    1. Severe errors that need attention (for instance, the sendmail
       panic message is buried in the formatted output).
    2. Statistics and counts
    3. Errors from sendmail invocation
    4. Errors from SMTP sessions
    5. Errors from messages
    6. Errors from recipients

    The logic behind 3 through 6 is that a sendmail process typically has
    many SMTP sessions, each SMTP session can have multiple messages,
    and each message can have multiple recipients.  So there is a logical
    hierarchy.

    Also, it's not clear to me if the intent is to catch severe errors,
    or if it is preferable to let them fall into the "Unmatched entries"
    category.

- For readability, I think the following two items would help:

   A. Report hosts as:
           From: [1.2.3.4] host.example.com (may be forged):  2 Times(s)
      instead of:
           From: host.example.com (may be forged) [1.2.3.4]:  2 Times(s)

      (even though sendmail prefers the latter - the problem is that
      the IP address is almost always definitive, whereas the hostname
      is often forged.  This way it's also easier to identify attacks
      from the same IP subnets, and it looks more tabulated.)

   B. Make use of more tabular format.  So you can report:

         From: [1.2.3.4] example.org:                       2 Time(s)
         From: [5.6.7.8] baz.org (may be forged)            2 Time(s)

      instead of:

         From: [1.2.3.4] example.org: 2 Time(s)
         From: [5.6.7.8] baz.org (may be forged) 2 Time(s)


- It might be useful to have a couple of switch arguments:
   A. For checking consistency or debugging (maybe that's what the
       --debug switch is for?)  For example, we could tag the lines
      for which no source is known, in case it ever gets matched.
      This would be most useful during some form of beta.
   B. For providing explanations or help.  For some errors, the
      user may not know what the error is from the one-liner that
      precedes its section.  A more detailed explanation or help
      on correction might be useful.  (Maybe the --detail switch
      can be used?)

- More consistency on newlines.  Some sections put them at the end,
   some at the beginning.  So the resulting output can have from
   one to three newlines, depending on the combination of
   sections reported.

On an related note, it might also be useful to have a mechanism for
users to define their own matching statements.  The reason is that
sendmail is often customized, and is not practical to embed code
in the script to address the changes to the stock sendmail that
everyone makes.  But I don't know how to do that gracefully (maybe
some variables read from a file that can be executed, where the variable
contains the user's code?)

-------------- next part --------------

##########################################################################
# $Id: sendmail,v 1.42 2004/10/29 21:19:01 kirk Exp $
##########################################################################
# $Log: sendmail,v $
# Revision 1.42  2004/10/29 21:19:01  kirk
# Added all changes that Mike Tremaine <mgt at stellarcore.net>
# so generously compiled for me...
#
# Revision 1.15  2004/10/15 18:30:15  mgt
# Added unknownusersthreshold import from sendmail.conf -mgt
#
# Revision 1.14  2004/10/11 18:08:48  mgt
# patches by hugo -mgt
#
# Revision 1.13  2004/10/07 21:25:51  mgt
# Fixed up ENV from sendmail.conf -mgt
#
# Revision 1.12  2004/08/20 02:24:15  mgt
# Fixed ENV -mgt
#
# Revision 1.11  2004/08/11 22:23:05  mgt
# GLobal threshholds and SPF Milter totals -mgt
#
# Revision 1.10  2004/08/11 00:38:49  mgt
# Added new floors as ENV -mgt
#
# Revision 1.9  2004/07/29 19:33:29  mgt
# Chmod and removed perl call -mgt
#
# Revision 1.8  2004/07/10 01:54:36  mgt
# sync with kirk -mgt
#
# Revision 1.41  2004/06/21 14:59:05  kirk
# Added tons of patches from Pawe? Go?aszewski" <blues at ds.pg.gda.pl>
#
# Thanks, as always!
#
# Revision 1.40  2004/06/21 14:18:55  kirk
# *** empty log message ***
#
# Revision 1.39  2004/06/21 13:57:13  kirk
# *** empty log message ***
#
# Revision 1.38  2004/02/03 18:39:34  kirk
# Patches from [ISO-8859-2] Pawe? Go?aszewski" <blues at ds.pg.gda.pl>
#
# Revision 1.37  2004/02/03 04:29:42  kirk
# Patch from Joe Digilio <jdigilio at earth.northwestern.edu>
#
# Revision 1.36  2004/02/03 04:10:21  kirk
# More patches from Mike Tremaine <mgt at stellarcore.net>
#
# Revision 1.35  2004/02/03 03:52:20  kirk
# Added mailscanner filter and more Solaris support from Mike Tremaine <mgt at stellarcore.net>
#
# Revision 1.34  2004/02/03 03:28:30  kirk
# Michael Stovenour <michael at stovenour.net>
#
# Revision 1.33  2004/02/03 02:45:26  kirk
# Tons of patches, and new 'oidentd' and 'shaperd' filters from
# Pawe? Go?aszewski" <blues at ds.pg.gda.pl>
#
##########################################################################

########################################################
# Please send all comments, suggestions, bug reports,
#    etc, to logwatch-devel at logwatch.org
########################################################

use Logwatch ':sort';

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

#Mailer names hash
my %Mailers;

my $MsgsSent = 0;
my $BytesTransferred = 0;
my $AddrRcpts = 0;
my $NumTimeoutSendWarnings = 0;
my $NumTimeoutSend = 0;
my $UserUnknown = 0;
my $TLSAcceptFailed = 0;
my $SaveMailPanic = 0;
my $RemoteProtocolError = 0;
my $ReturnReceipt = 0;
my $TooManyRcpts = 0;
my $CantCreateOutput = 0;
my $OutdatedAliasdb = 0;
my $MaxLoadAvg = 0;
my $LoadAvgReject = 0;
my $LoadAvgQueueSkip = 0;
my $SendmailStarts = 0;
my $Defang = 0;
my $OverSize = 0;
my $OverSizeBytes = 0;
my $NoMilterFilters = 0;
my $XS4ALL = 0; # don't know how this is used
my $ukusers = 0;
my $rldeny = 0;
my $knspam = 0;
my $blktotal = 0;
my $domainer = 0;
my $ukndomain = 0;
my $uknhosts = 0;
my $chkmreject = 0;
my $chkrereject = 0;
my $pregreetnum = 0;

my @SizeNames;
my @SizeDist;

my %relay;
my %abuse;
my %largeHdrs;
my %notLocal;
my %MailRejected;
my %SPFTotal;
my %StarttlsCert;
my %CommandUnrecognized;
#my %SizeDist;
my %LargeMsgs;
my %Starttls;
my %PREGreeting;


# the declaration of variables needs to be outside the loop, so it's not clear it
# helps us to leave the initialization inside it
if ($Detail >= 10) {
   # Initialise the Size distribution array
   @SizeNames = ('0 - 10k', '10k - 20k', '20k - 50k', '50k - 100k',
                 '100k - 500k', '500k - 1Mb', '1Mb - 2Mb', '2Mb - 5Mb',
                 '5Mb - 10Mb', '10Mb+');

} # if

# Initialize the STARTTLS verification results
$Starttls{'server'} = [0, 0, 0, 0, 0];
$Starttls{'client'} = [0, 0, 0, 0, 0];
# Initialize $SizeDist array
for my $i (0..9) {
   $SizeDist[$i]{'Num'} = 0;
   $SizeDist[$i]{'Bytes'} = 0;
}


# Unknown users with bounces <= $UnknownUsersThreshold will only be
# printed if the detail level is >= 10. Setting this value in sendmail.conf
# it. Default is 0 -mgt
my $UnknownUsersThreshold = 0;
if ( $ENV{'sendmail_unknownusersthreshold'} ) { $UnknownUsersThreshold = "$ENV{'sendmail_unknownusersthreshold'}"; };

while (defined($ThisLine = <STDIN>)) {
   # not all log entries have a queue id
   #($QueueID) = ($ThisLine =~ /^([a-zA-Z0-9]+): / );
   ($QueueID) = ($ThisLine =~ /^(\w{8}\d{6}): / );
   if (defined $QueueID) {$ThisLine =~ s/^$QueueID: //;}
   if (
      # file=alias.c, LogLevel>7, LOG_NOTICE
      ( $ThisLine =~ m/^alias database [^ ]* (auto)?rebuilt by/ ) or
      # file=alias.c, LogLevel>7, LOG_INFO
      ( $ThisLine =~ m/[0-9]* aliases, longest [0-9]* bytes, [0-9]* bytes total/ ) or
      # file=util.c, LogLevel>9, LOG_INFO
      ( $ThisLine =~ m/^started as: / ) or
      # file=daemon.c, LogLevel>9, LOG_INFO
      ( $ThisLine =~ m/stopping daemon, reason=/ ) or

      # this error is now counted later
      #( $ThisLine =~ m/^collect: premature EOM/ ) or
      # this error is now counted later
      #( $ThisLine =~ m/collect: (unexpected close|I\/O error|read timeout) on connection from (.*)?, / ) or
      # this error is now counted later
      #( $ThisLine =~ m/timeout waiting for input/ ) or
      # this error is now counted later
      #( $ThisLine =~ m/lost input channel from/ ) or
      # this error is now counted later
      # ( $ThisLine =~ m/DSN: Cannot send message for \d+ day/ ) or
      #tThis one appears to be the result of EX_UNAVAILABLE; and I believe it is now counted later
      # ( $ThisLine =~ m/: Service unavailable$/) or


# I couldn't identify the sources of the following four statements:
# This one appears to be the result of a file/socket read; it's not clear to me why we want to ignore it
      ( $ThisLine =~ m/Broken pipe|Connection (reset|timed out)/ ) or
# this appears to be the result of adding a header by a milter program; it should be taken care of by
#   the milter statements
      ( $ThisLine =~ m/X-Spam/ ) or
# this last one I can't identify...
      ( $ThisLine =~ m/Milter: from=/ ) or


      # file=milter.c, LogLevel>8, LOG_INFO
      ( $ThisLine =~ m/^Milter message: body replaced$/ ) or
      # file=milter.c, LogLevel>8, LOG_INFO
      (( $ThisLine =~ m/Milter (add|change|insert|delete): /) and not
         # the following is a hack to detect the X-Scanned-By later
         ( $ThisLine =~ m/Milter: add: header: X-Scanned-By: MIMEDefang/) ) or
      # file=milter.c, LogLevel>9, LOG_INFO
      ( $ThisLine =~ m/^Milter accept: message$/ ) or
      # file=milter.c, LogLevel>9, LOG_INFO
      ( $ThisLine =~ m/Milter \(\w*\): init success to / ) or
      # file=milter.c, LogLevel>9, LOG_INFO
      ( $ThisLine =~ m/Milter: connect/ ) or
      # file=milter.c, LogLevel>10, LOG_INFO
      ( $ThisLine =~ m/Milter \(\w*\): abort filter/ ) or
      # file=milter.c, LogLevel>10, LOG_INFO
      ( $ThisLine =~ m/milter=\w*, action=\w*, accepted/ ) or
      # file=milter.c, LogLevel>12, LOG_INFO
      ( $ThisLine =~ m/milter=\w*, action=\w*, continue/ ) or
      # the following is captured later in srvrsmtp.c, except for milter service name
      # file=milter.c, LogLevel>12, LOG_INFO
      ( $ThisLine =~ m/milter=(\w*), (reject|discard)/) or
      # file=milter.c, LogLevel>14, LOG_INFO
      ( $ThisLine =~ m/Milter: (rcpts|senders):/ ) or
      # file=milter.c, LogLevel>17, LOG_INFO
      ( $ThisLine =~ m/Milter \(\w*\): (headers|body) sen[dt]/ ) or
      # file=milter.c, LogLevel>18, LOG_INFO
      ( $ThisLine =~ m/Milter \(\w*\): quit filter/ ) or
      # file=milter.c, LogLevel>21, LOG_INFO
      ( $ThisLine =~ m/Milter \(\w*\): time command / ) or

      # the following two return errors that are caught in the "to=" statement
      # file=srvrsmtp.c, LogLevel>3, LOG_INFO
      ( $ThisLine =~ m/Milter: data, reject=554 5\.7\.1 (.*)/) or
      # file=srvrsmtp.c, LogLevel>3, LOG_INFO
      ( $ThisLine =~ m/Milter: data, discard$/) or

      # SMTP codes
      # status code 0XX is informational
      ( $ThisLine =~ m/^--- 0[0-9]{2}(-| )/ ) or
      # status code 2XX is success - but hold onto the Hello response
      ( ( $ThisLine =~ m/^--- 2[0-9]{2}(-| )/ ) and not
          ( $ThisLine =~ /^--- 250[ -].* Hello .*, pleased to meet you$/)) or
      # status codes 4XX are for transient failures
      ( $ThisLine =~ m/^--- 4[0-9]{2}(-| )/ ) or
      # status code 354 used to request data
      ( $ThisLine =~ m/^--- 354 Enter mail, end with \"\.\" on a line by itself/ ) or
      # invalid smtp commands detected later ($RejCmd)
      ( $ThisLine =~ m/^--- 502 5(\.[0-9]){2} Sorry, we do not allow this operation$/ ) or      
      # Need RCPT most likely because of incorrect RCPT command, in which case ignore it
      ( ( $ThisLine =~ m/^--- 503 5(\.[0-9]){2} Need RCPT \(recipient\)$/) and
          ( $Msgs{$QueueID}{"BadRCPT"} > 0)) or
      # Commands rejected are from greet_pause or milter
      ( $ThisLine =~ m/^--- 550 5(\.[0-9]){2} Command rejected$/ ) or
      # User unknown detected later by ruleset=check_rcpt
      ( $ThisLine =~ m/^--- 550 5(\.[0-9]){2} .*\.\.\. User unknown/ ) or
      # Relaying denied detected later by ruleset=check_rcpt
      ( $ThisLine =~ m/^--- 550 5(\.[0-9]){2} .*\.\.\. Relaying denied/ ) or
      # Access denied detected later by ruleset=check_relay
      ( $ThisLine =~ m/^--- 550 5(\.[0-9]){2} Access denied/ ) or
      # Domain errors detected later by ruleset=check_mail
      ( $ThisLine =~ m/^--- 553 5(\.[0-9]){2} .*\.\.\. Domain of sender address .* does not exist$/ ) or
      ( $ThisLine =~ m/^--- 553 5(\.[0-9]){2} .*\.\.\. Domain name required for sender address/ ) or
      # the following used by milter, which is detected later
      ( $ThisLine =~ m/^--- 554 5\.7\.1 / ) or
      # the following used greet_pause feature
      ( $ThisLine =~ m/^--- 554 .* not accepting messages/ ) or
      # these are the valid commands
      ( $ThisLine =~ m/<-- (EHLO|HELO|STARTTLS|MAIL FROM|RCPT TO|DATA|RSET|QUIT|NOOP|ETRN|VERB|EXPN|VRFY|HELP|AUTH|NOOP|VERB)/i ) or
      # file=daemon.c, LogLevel>11, LOG_INFO
      ( $ThisLine =~ m/SMTP outgoing connect on/ ) or
      # file=envelope.c, LogLevel>9, LOG_INFO
      ( $ThisLine =~ m/done; delay=[0-9:\+]*, ntries=/ ) or
      # file=alias.c, LogLevel>10, LOG_INFO
      ( $ThisLine =~ m/^alias.*=>/ ) or
      # file=main.c, LogLevel>9, LOG_INFO
      ( $ThisLine =~ m/connect from / ) or
      # file=srvrsmtp.c, LogLevel>11, LOG_INFO
      ( $ThisLine =~ m/AUTH: available mech=/ ) or
# we should probably count the following...
      # file=deliver.c, LogLevel>9, LOG_INFO
      ( $ThisLine =~ m/AUTH=client, relay=.*, mech=.*, bits=\d*/ ) or
      # file=srvrsmtp.c, LogLevel>11, LOG_INFO
      ( $ThisLine =~ m/AUTH=server, relay=.*, authid=.*, mech=.*, bits=\d*/ ) or

      # this should have been taken care of by the earlier milter statements.  Note that as written,
      #   this statement ignores all sorts of errors
      ( $ThisLine =~ m/Milter \(spfmilter\)/ ) or

# we should probably count the following...
      # file=deliver.c, LogLevel>4, LOG_INFO
      ( $ThisLine =~ m/^discarded$/ ) or

      # Ignore these lines for now...
      # Dec 31 04:03:01 tp760 sendmail[26884]: STARTTLS=client, relay=[127.0.0.1], version=TLSv1/SSLv3, verify=FAIL, cipher=EDH-RSA-DES-CBC3-SHA, bits=168/168
      # Dec 31 04:03:01 tp760 sendmail[26887]: STARTTLS=server, relay=tp760.stovenour.net [127.0.0.1], version=TLSv1/SSLv3, verify=NO, cipher=EDH-RSA-DES-CBC3-SHA, bits=168/168

      # file=tls.c, LogLevel>14, LOG_INFO
      ( $ThisLine =~ m/^STARTTLS=(server|client), get_verify:/ ) or
      # file=tls.c, LogLevel>11, LOG_INFO
      ( $ThisLine =~ m/^STARTTLS=(server|client), cert-subject=/ ) or
      # file=tls.c, LogLevel>13, LOG_INFO
      ( $ThisLine =~ m/^STARTTLS=(server|client), Diffie-Hellman init, key=/ ) or
      # file=tls.c, LogLevel>12, LOG_INFO
      ( $ThisLine =~ m/^STARTTLS=(server|client), init=1/ ) or
      # file=deliver.c, LogLevel>13, LOG_INFO
      ( $ThisLine =~ m/^STARTTLS=client, start=ok$/ ) or
      # file=sendmail.cf
      ( $ThisLine =~ m/^ruleset=trust_auth, .* reject=550 5\.7\.1 .*\.\.\. not authenticated$/  ) or
      # file=queue.c, LogLevel>8, LOG_INFO
      ( $ThisLine =~ m/^runqueue: Flushing queue from/ ) or
      # these are now counted later...
      #( $ThisLine =~ m/^SYSERR\(root\): collect: I\/O error on connection from / ) or
      # file=daemon.c, LogLevel>8, LOG_INFO
      ( $ThisLine =~ m/^accepting connections again for daemon / ) or
# do we want to count these?
      # file=srvrsmtp.c, LogLevel>-1, LOG_INFO
      ( $ThisLine =~ m/probable open proxy: / ) or
      # the following return error is caught in the "to=" statement
      ( $ThisLine =~ m/^makeconnection \(.*\) failed: /)


   ) {
      # We don't care about these statements above
   # file=srvrsmtp.c
   } elsif ( ($RelayHost) = ($ThisLine =~ /^--- 250[ -].* Hello (.*), pleased to meet you$/) ) {
      # record the host for errors on SMTP commands
      $Msgs{$QueueID}{"Relay"} = $RelayHost;
      $Msgs{$QueueID}{"BadRCPT"} = 0;
   # file=headers.c, LogLevel>-1, LOG_INFO
   } elsif ( ($FromUser, $Bytes, $NumRcpts, $RelayHost) = ($ThisLine =~ /^from=(.*?), .*size=([0-9]+),.*nrcpts=([0-9]+).*relay=(.*)/) ) {
      if ($NumRcpts > 0) {
         $MsgsSent++;
         $AddrRcpts += $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 info from message to a hash
      $Msgs{$QueueID}{"Relay"} = $RelayHost;
      $Msgs{$QueueID}{"FromUser"} = $FromUser;
      $Msgs{$QueueID}{"Size"} = $Bytes;

   # file=deliver.c, LogLevel>-1, LOG_INFO
   } elsif ( ($ToUser, $MailerString, $DeliverStat) = ($ThisLine =~ m/^to=(.*?), (.*)stat=(.*)/ ) ) {
      if ( $DeliverStat =~ /^Sent/ ) {
     	   (($MailerType) = ( $MailerString =~ /mailer=(.*?),/));
     	   (($RelayName) = ( $MailerString =~ /relay=(.*?),/));
         $MailerType =~ s/^\s*$/\(unspecified\)/;
         # remove the entries from MSP (Mail Submission Program) relay to
         #    localhost
         if (($MailerType =~ /^relay$/) and
                  ($RelayName =~ /\[127\.0\.0\.1\]/)) {
            $RelayLocalhost++;
         } else {
            $Mailers{$MailerType}++;
         } # if $MailerType !~ /^relay$/ ...

         if (defined $Msgs{$QueueID}{"Size"}) {
            if ($Msgs{$QueueID}{"Size"} > 5242880) {  #10485760
               $LargeMsgs{$Msgs{$QueueID}{"FromUser"} . " \-\> " .$ToUser}++;
            } # if size > 5242880
         } # if defined
      } elsif ( $DeliverStat =~ /^queued$/ ) {
      # do nothing if being queued
      } elsif ( ($Reason) = ( $DeliverStat =~ /^Deferred: (.*)/ ) ) {
          $StatDeferred{$Reason}{$ToUser}++;
      }  elsif ( ($Reason) = ( $DeliverStat =~ /(.*)/ ) ) {
          $StatRejected{$Reason}{$ToUser}++;
      }
   } elsif ((($NewQueueID, $Reason) = ( $ThisLine =~ m/^(\w{8}\d{6}): (?:return to sender|sender notify|postmaster notify|DSN): (.*)/ )) and
       (defined $StatRejected{$Reason})) {
          # this is a type of error that has been logged, but it is creating a new message
          $Msgs{$NewQueueID}{"Relay"} = $Msgs{$QueueID}{"Relay"};
          $Msgs{$NewQueueID}{"Size"} = $Msgs{$QueueID}{"Size"};
          $Msgs{$NewQueueID}{"FromUser"} = "system_notify";
   # file=deliver.c, LogLevel>4, LOG_INFO
      } elsif ( ($NewQueueID, $Owner) = ( $ThisLine =~ m/(\w{8}\d{6}): clone: owner=(.*)/ ) ) {
          $Msgs{$NewQueueID}{"FromUser"} = $Owner;
   # file=envelope.c
   } elsif ( $ThisLine =~ m/(return to sender|sender notify|postmaster notify|DSN): Warning: could not send message for past (.*)/ ) {
      $TimeoutSendWarning = $2;
      $NumTimeoutSendWarnings++;
   } elsif ( $ThisLine =~ m/(return to sender|sender notify|postmaster notify|DSN): Cannot send message for (.*)/ ) {
      $TimeoutSend = $2;
      $NumTimeoutSend++;
   } elsif ($ThisLine=~ /(return to sender|sender notify|postmaster notify|DSN): Return receipt/) {
      $ReturnReceipt++;
   # file=main.c, LogLevel>-1, LOG_INFO
   } elsif ( $ThisLine =~ m/^starting daemon/) {
      $SendmailStarts++;
   # file=collect.c, LogLevel>1, LOG_WARNING
   } elsif ( ($Reason) = ($ThisLine =~ /^collect: premature EOM: (.*)/) ) {
      $Source = "Processing $QueueID";
      $CollectError{$Reason}{$Source}++;
   # file=collect.c, LogLevel>0, LOG_NOTICE
   } elsif ( ($Reason, $Source) = ($ThisLine =~ /^collect: (unexpected close|I\/O error|read timeout) on connection from (.*)?, /) ) {
      $CollectError{$Reason}{$Source}++;
# I couldn't identify the sources of the following (I suspect is from the MIMEDefang milter.  But it seems
#   to me that what we really want is mail rejected from scanning, not the number scanned).  The new milter
#   code catches these rejects and discards, so I don't think this is necessary anymore
   } elsif ( $ThisLine =~ m/X-Scanned-By: MIMEDefang/) {
      $Defang++;
   # file=collect.c, LogLevel>6, LOG_NOTICE
   } elsif (($Size) = ($ThisLine =~ m/^message size \(([0-9]+)\) exceeds maximum/)) {
      $OverSize++;
      $OverSizeBytes += $Size;
# The following is also on the return code of the to= statement, so it is a duplicate
   #} elsif ( ($Host) = ($ThisLine =~ /\(Name server: ([^ ]+): host not found\)/)) {
   #   $UnknownHosts{$Host}++;
   # this one is done later in check_mail ruleset
   #} 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)

# Note (-bl): this is the same as the later check_rcpt, except that the word Denied is capitalized here.
#       So to avoid confusion I suggest that we use the REJECT label, as shown in the next elsif block
      $KnownSpammer{$1}++;
   # file: access
   } elsif ($ThisLine =~ /ruleset=check_relay, arg1=([^,]*),.* reject=550 5\.7\.1 Access denied/) {
      # We block some particularly annoying spam domains with the
      # following in /etc/mail/access...
      # From:worduphosting.com  ERROR:550 5.7.1 Access denied
      # Remember the error message is user defined in /etc/mail/access
      # So if anyone can make a better check please do -mgt

# Note (-bl): the same output is achieved by using the label REJECT in /etc/mail/access file:
#       From:worduphosting.com   REJECT
      $KnownSpammer{$1}++;
# I couldn't identify the sources of the following:
   } elsif (
      ($Host) = ($ThisLine =~ /relay=([^ ]+ \[[^ ]+\]), reject=553 5\.3\.0 .*/) or
      ($Host) = ($ThisLine =~ /relay=([^ ]+ \[[^ ]+\] \(may be forged\)), reject=553 5\.3\.0 .*/)
   ) {
      $KnownSpammer{$Host}++;
   # file: sendmail.cf
   } elsif ( ($User) = ($ThisLine =~ /^ruleset=check_rcpt, arg1=([^,]*), relay=[^,]*, reject=550\s*[\d.]*\s*[^ ]*\.\.\. Mailbox disabled for this recipient/) ) {
      $DisabledMailbox{$User}{$QueueID}++;
      $Msgs{$QueueID}{"BadRCPT"}++;
   # test for unknown relay users (users we would have relayed elsewhere)
   # file: sendmail.cf
   } elsif ( ($User) = ($ThisLine =~ /^ruleset=check_rcpt, arg1=(.*?), .*... User unknown$/) ) {
      $UnknownUserscheckrcpt{$User}{$QueueID}++;
      $Msgs{$QueueID}{"BadRCPT"}++;
   } elsif ( ($User) = ($ThisLine =~ /^(.*)\.\.\. (User unknown|No such user( here)?)$/i) ) {
      $UnknownUsers{lc $User}{$QueueID}++;
   # file: sendmail.cf
   } elsif ( ($Dest,$Relay) = ($ThisLine =~ /^ruleset=check_rcpt, arg1=([^,]*), relay=([^,]*)(?: \(may be forged\))?, reject=550\s*[\d.]*\s*.*\.\.\. Relaying denied/) ) {
      $Temp = "From " . $Relay . " to " . $Dest;
      $RelayDenied{$Temp}++;
      $Msgs{$QueueID}{"BadRCPT"}++;

# I don't know where the following blacklist/blackhole statements are generated from
   } 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}++;
      $Msgs{$QueueID}{"BadRCPT"}++;

   # file=sendmail.cf
   } elsif ( ($User)  = ($ThisLine =~ /^--- 553 5(?:\.\d){2} (.*)\.\.\. Hostname required$/) ) {
      $DomainErrors{$Msgs{$QueueID}{"Relay"}}{$User . " (missing)"}++;
   # file: sendmail.cf
   } elsif ( ($User) = ($ThisLine =~ /^ruleset=check_mail, arg1=(.*), relay=[^,]*, reject=451\s*[\d.]*\s*Domain of sender address .* does not resolve/) ) {
      $DomainErrors{$Msgs{$QueueID}{"Relay"}}{$User . ": (does not resolve)"}++;
   # file: sendmail.cf
   } elsif ( ($User) = ($ThisLine =~ /^ruleset=check_mail, arg1=(.*), relay=[^,]*, reject=553\s*[\d.]*\s*.*\.\.\. Domain of sender address .* does not exist/) ) {
      $DomainErrors{$Msgs{$QueueID}{"Relay"}}{$User . " (does not exist)"}++;
   # file: sendmail.cf
   } elsif ( ($User) = ($ThisLine =~ /^ruleset=check_mail, arg1=(.*), relay=[^,]*, reject=553\s*[\d.]*\s*.*\.\.\. Domain name required for sender address .*/) ) {
      $DomainErrors{$Msgs{$QueueID}{"Relay"}}{$User . " (missing)"}++;

   # test for all kinds of rejects due check_mail
   #h2G22Jq19062: ruleset=check_mail, arg1=<3popmeywsv at 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__ at 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.
   # file: sendmail.cf
   } 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 at 163.net>, relay=[218.25.142.7], reject=450 4.7.1 <zengcheng2 at 163.net>... Relaying temporarily denied. Cannotresolve PTR record for 218.25.142.7
   # file: sendmail.cf
   } elsif( ($arg,$relay,$reason) = ($ThisLine =~ /^ruleset=check_rcpt, arg1=(.*), relay=.*?\[(.*)\].*, reject=(.*)/) ) {
       $reason =~ s/$arg\.\.\. //;
       $Temp = "$arg ($reason)";
       $CheckRcptReject{$Temp}++;
       $Msgs{$QueueID}{"BadRCPT"}++;
   #h2G4jUx22325: lost input channel from localhost [127.0.0.1] to MTA after rcpt
   # file=srvrsmtp.c, LogLevel>1, LOG_NOTICE
   } elsif ( ($Temp)  = ($ThisLine =~ /^lost input channel from (.*) to .* after .*/) ) {
       $LostInputChannel{$Temp}++;
   #h2G2FUx19181: timeout waiting for input from mail.bpsmailer.com. during client greeting
      # file=collect.c, LogLevel>2, LOG_NOTICE
      # file=control.c, LogLevel>2, LOG_NOTICE
      # file=util.c, LogLevel>1, LOG_NOTICE
   } elsif ( ($Temp)  = ($ThisLine =~ /^(timeout waiting for input .*)/) ) {
       $TimeoutWaiting{$Temp}++;
   # file=milter.c, LogLevel>10, LOG_INFO
   } elsif ( $ThisLine =~ m/Milter: no active filter/) {
       $NoMilterFilters++;
   # file=srvrsmtp.c
   } elsif ( ($Temp) = ($ThisLine=~ /\-\-\- 500 5\.5\.1 Command unrecognized: \"(.*)\"/) ) {
      # first we try to delete it from the list of Unmatched Entries
      $Temp1 = "<-- " . $Temp;
      if ($OtherList{$Temp1} > 0) {
         if ($OtherList{$Temp1} == 1) {
            delete ($OtherList{$Temp1});
         } else {
            $OtherList{$Temp1}--;
         }
      }
      if (not defined $CommandUnrecognized{$QueueID}) {
         $CommandUnrecognized{$QueueID} = "";
      }
      if ($Temp =~ /^$/) { $Temp = "<Empty Line>"};
      $CommandUnrecognized{$QueueID} =  $CommandUnrecognized{$QueueID} . "\t\t" . $Temp . "\n";

   #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
   # file=srvrsmtp.c, LogLevel>5, LOG_INFO
   } elsif ( ( $Host ) = ($ThisLine =~ /(.*) (\(may be forged\) )?did not issue MAIL\/EXPN\/VRFY\/ETRN during connection to (M[TS]A|Daemon0)/) ) {
      # we test if they previously sent junk, because the connection is expected to fail
      if ($CommandUnrecognized{$QueueID}) {
         $CommandUnrecognized{$QueueID} = $CommandUnrecognized{$QueueID} . "\t" . "... and then exited without communicating\n";
      } else {
      $DummyConnection{$Host}++;
      }
   # file=srvrsmtp.c, LogLevel>-1, LOG_INFO
   } elsif ($ThisLine =~ /rejecting commands from .+ \[(\d+.\d+.\d+.\d+)\] due to pre-greeting traffic/ ) {
      $PREGreeting{$1}++;
      $PREGreetingQueue{$QueueID}++;
   # I think this is redundant
   #} elsif ( ($Host)  = ($ThisLine =~ /^([^ ]*) did not issue .*? during connection to (MTA|Daemon0)/) ) {
   #   $DummyConnection{$Host}++;

   #hA29V0hK013676: hnexfe06.hetnet.nl [195.121.6.172]: Possible SMTP RCPT flood, throttling.
   # file=srvrsmtp.c, LogLevel>5, LOG_INFO
   } elsif ( ($Temp)  = ($ThisLine =~ /^.*\[(.*?)\]: Possible SMTP RCPT flood, throttling./) ) {
      $BadRcptThrottle{$Temp}++;
   # file=srvrsmtp.c
   } elsif ($ThisLine =~ /^Too many recipients$/) {
      $TooManyRcpts++;
   #h2GKtU001122: DSN: Too many hops 26 (25 max): from <MAILER-DAEMON at atbusiness.com> via localhost, to <vlagrarycf at pacific.net.in>
   #h2GHtSx30926: SYSERR(root): Too many hops 26 (25 max): from <MAILER-DAEMON at atbusiness.com> via localhost, to <superstore at bpsmailer.com>
   # file=deliver.c (note: while this is a syserr, I think it's reasonable to extract it here, as there is not
   #                 much the sender can do if they don't own the recipient address
   } elsif ( ($Temp)  = ($ThisLine =~ /^.*?Too many hops (.*)/) ) {
       $TooManyHops{$Temp}++;
   # file=main.c LogLevel>3, LOG_INFO
   } elsif ( ($Warning)  = ($ThisLine =~ /Authentication-Warning: (.*)/) ) {
      $AuthWarns{$Warning}++;
   # file=alias.c, LogLevel>2, LOG_ERR
   } elsif ( ($Forward,$Error) = ($ThisLine =~ /^forward ([^ ]*): transient error: (.*)/) ) {
      $Temp = $Forward . ": " . $Error;
      $ForwardErrors{$Temp}++;
   # file=alias.c, LogLevel>2,10, LOG_WARNING
   } elsif ( ($Forward,$Error) = ($ThisLine =~ /^forward ([^ ]*): (.*)/) ) {
      $Temp = $Forward . ": " . $Error;
      $ForwardErrors{$Temp}++;
# I couldn't identify the sources of the following three blocks:
   } 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}++;
   # file=collect.c, LogLevel>-1,  LOG_NOTICE
   } elsif ($ThisLine=~ /^headers too large .* from (.*) during message collect$/) {
      $largeHdrs{$1}++;
   # file=srvrsmtp.c, LogLevel>5, LOG_INFO
   } elsif ($ThisLine=~ /(\S+) \[([0-9\.]+)]: (\S+) (\S+) \[rejected\]/i) {
      chomp($host=$2." ". (defined($1) ? "(".$1.")" : "(unresolved)") );
      $luser=$4;
      $RejCmd=uc $3;
      $abuse{$host}{$luser}{$RejCmd}++;
   # I think we should pick this up from the to= statement, and discard this one
   } elsif ( $ThisLine =~ m/(DSN|postmaster notify|return to sender|sender notify): User unknown/ ) {
      $UserUnknown++;
   # counted earlier
   #} elsif ( $ThisLine =~ m/timeout waiting for input from (\S+)/ ) {
   #   $Timeouts{$1}++;
   } elsif ( $ThisLine =~ m/timeout writing message to (\S+?)\.?:/ ) {
      $Timeouts{$1}++;
   # file=srvrsmtp.c, LogLevel>5, LOG_INFO
   } elsif ( $ThisLine =~ /\[([0-9\.]+)]: ETRN (\S+)/ ) {
      chomp($ETRN=$2." from ".$1);
      $ETRNs{$ETRN}++;
   # file=conf.c, LogLevel>8, LOG_NOTICE
   } elsif ( $ThisLine =~ /rejecting connections on daemon [^ ]+: load average: ([0-9]+)/ ) {
      $LoadAvg{$1}++;
      $LoadAvgReject++;
   } elsif (
      # file=queue.c, LogLevel>8, LOG_INFO
      ($ThisLine =~ /Aborting queue run: load average too high/ ) or
      # file=queue.c, LogLevel>8, LOG_INFO
      ($ThisLine =~ /Skipping queue run -- load average too high/ )
   ){
      $LoadAvgQueueSkip++;
# I couldn't identify the sources of the following:
   } elsif ($ThisLine=~ /reject=.*MESSAGE NOT ACCEPTED - (.+)/) {
      chomp($host=$1);
      $MailRejected{$host}++;
   # file=stats.c, LogLevel>12, LOG_INFO
   } elsif ( ($StatFile, $StatError) = ($ThisLine=~ /^poststats: (.*?): (.*)/) ) {
      $StatFileError{$StatFile}{$StatError}++;
   # file=tls.c, LogLevel>12, LOG_WARNING
   } elsif ( ($TLSFile) = ($ThisLine=~ /STARTTLS: (.*) missing/) ) {
      $TLSFileMissing{$TLSFile}++;
   # file=srvrsmtp.c, LogLevel>5, LOG_WARNING
   } elsif ($ThisLine=~ /STARTTLS=server, error: accept failed=/) {
      $TLSAcceptFailed++;
   # file=srvrsmtp.c, LogLevel>8, LOG_WARNING
   } elsif ( ($TLSReason) = ($ThisLine=~ /STARTTLS=server: \d*:error:\d{8}:[^:]*:[^:]*:([^:]*):/) ) {
      $TLSFailed{$TLSReason}++;
   # file=tls.c, LogLevel>-1, LOG_INFO
   } elsif (($StarttlsReason) = ($ThisLine =~ /^STARTTLS: (?:x509|TLS) cert verify: depth=[0-9]+ .*, state=[0-9]+, reason=(.*)$/ )) {
      $StarttlsCert{$StarttlsReason}++;
   # file=tls.c, LogLevel>8, LOG_INFO
   } elsif ( ($StarttlsMode, $StarttlsVerify, $StarttlsCipherType, $StarttlsNumBits) =
      ($ThisLine =~ /^STARTTLS=(server|client), relay=.*, version=.*, verify=(\w*), cipher=(.*), bits=(\w*\/\w*)/) ) {
      # ignore "NO", "NOT", "FAIL", "NONE", since no authentication granted
      if (($StarttlsVerify =~ /^NO/i) or ($StarttlsVerify =~ /FAIL/))  {
      } elsif ($StarttlsVerify =~ /OK/i) {
         $Starttls{$StarttlsMode}[0]++;
      } elsif ($StarttlsVerify =~ /TEMP/i) {
         $Starttls{$StarttlsMode}[1]++;
      } elsif ($StarttlsVerify =~ /PROTOCOL/i) {
         $Starttls{$StarttlsMode}[2]++;
      } elsif ($StarttlsVerify =~ /SOFTWARE/i) {
         $Starttls{$StarttlsMode}[3]++;
      } else {
         $Starttls{$StarttlsMode}[4]++;
      }
      $StarttlsCipher{"Cipher: " . $StarttlsCipherType . " Bits: " . $StarttlsNumBits}++;
   # file=queue.c, LogLevel>-1, LOG_ALERT
   } elsif ($ThisLine=~ /savemail panic/) {
      $SaveMailPanic++;
   # this appears to be the result of EX_PROTOCOL return code, so it should be handled with other EX_ messages
   } elsif ($ThisLine=~ /Remote protocol error/) {
      $RemoteProtocolError++;
# I couldn't identify the sources of the following:
   } elsif ($ThisLine=~ /ruleset=check_XS4ALL/) {
      $XS4ALL++;
   } elsif (
      # file=util.c, LogLevel>-1, LOG_NOTICE
      (($Host,$Attack) = ($ThisLine =~ /POSSIBLE ATTACK from ([^ ]+): (.*)/)) or
      # file=srvrsmtp.c, LogLevel>5, LOG_INFO
      (($Host,$Attack) = ($ThisLine =~ /([^ ]+ \[[^ ]+\]): possible SMTP attack: (.*)$/))
   ) {
      $AttackAttempt{$Host}{$Attack}++;
   #file=headers.c, LogLevel>-1, LOG_ALERT
   } elsif (($Attack) = ($ThisLine =~ /^(.*) \(possible attack\)$/)) {
      $AttackAttempt{"UNKNOWN"}{$Attack}++;
   # file=usersmtp.c, LogLevel>8, LOG_WARNING
   } elsif ( ($File,$Error) = ($ThisLine =~ /^safesasl\(([^ ]+)\) failed: (.*)$/) ) {
      $SaslError{$File}{$Error}++;
   # can't find the following
   } elsif ( $ThisLine =~ m/Can\'t create output/ ) {
      $CantCreateOutput++;
   # file=alias.c, LogLevel>3, LOG_INFO
   } elsif ( $ThisLine =~ m/alias database [^ ]+ out of date/ ) {
      $OutdatedAliasdb++;
# The following two SYSERRs should probably be handled the same...
   # file=collect.c, LogLevel>0, LOG_CRIT
   } elsif ( ($User,$Uid) = ($ThisLine =~ /^SYSERR\(([^ ]+)\): collect: Cannot write [^ ]+ \([^ ]+, uid=(\d+), gid=\d+\): Disk quota exceeded/) ) {
      $Temp = "$User (uid=$Uid)";
      $QuotaExceed{$Temp}++;
   # file=queue.c, LogLevel>0, LOG_CRIT
   } elsif ( ($User,$Uid) = ($ThisLine =~ /^SYSERR\(([^ ]+)\): queueup: cannot create queue file [^ ]+, euid=(\d+): Disk quota exceeded/) ) {
      $Temp = "$User (uid=$Uid)";
      $QuotaExceed{$Temp}++;
   } elsif (
      # file=parseaddr.c, LogLevel>3, LOG_NOTICE
      ($Address,$Reason) = ($ThisLine =~ /^Syntax error in mailbox address "(.+)" \(([^ ]+)\)/) or
      # file=sendmail.cf
      ($Address,$Reason) = ($ThisLine =~ /^<(.+)>... (Colon illegal in host name part)/) or
      # file=parseaddr.c, LogLevel>3, LOG_NOTICE
      ($Reason,$Address) = ($ThisLine =~ /^(8-bit character in mailbox address) "<(.+)>"/)
   ) {
      $AddressError{$Reason}{$Address}++;
   # don't know where this is from
   } elsif ($ThisLine =~ /Received-SPF: (\w+)/ ) {
      $SPFTotal{$1}++;
   } else {
      $ThisLine =~ s/.*\: (DSN\: .*)/$1/;
      $ThisLine =~ s/.*\: (postmaster notify\: .*)/$1/;
      chomp($ThisLine);
      # Report any unmatched entries...
      $OtherList{$ThisLine}++; 
   }
}

#######################################################

if ($SendmailStarts > 0) {
   print "\nSendmail was started $SendmailStarts time(s)";
}

if (keys %TLSFileMissing) {
   print "\n\nWarning: Missing STARTTLS files:";
   foreach $TLSFile (keys %TLSFileMissing) {
      print "\n    $TLSFile";
   }
}

if (keys %StatFileError) {
   print "\n\nWarning: Error opening statistics files:";
   foreach $StatFile (sort keys %StatFileError) {
      print "\n    $StatFile:";
      foreach $StatError (keys %{$StatFileError{$StatFile}}) {
         print "\n        $StatError";
      }
   }
}

if ($NoMilterFilters > 0) {
   print "\n\nNo active milter filters\n";
}

if ($MsgsSent > 0) {
   print "\n\nBytes Transferred:      $BytesTransferred";
   print "\nMessages Processed:     $MsgsSent";
# Each explicitely addressed recipient in an email is counted as an
# "Addressed Recipient"
   print "\nAddressed Recipients:   $AddrRcpts";
}

# Message recipients are the actual recipients - one for each
# recipient to which to which a copy of email is sent
if (($Detail >= 10) and (keys %Mailers)) {
   @DefinedMailers = ('smtp', 'esmtp', 'smtp8', 'dsmtp', 'relay', 'procmail',
     'local', 'prog', '*file*');
   print "\n\nMessage recipients per delivery agent:";
   $TotalNum = 0;
   print "\nName          # Rcpts";
   # first we print the common mailers, to maintains some logical grouping
   foreach $MailerName (@DefinedMailers) {
     if ($Mailers{$MailerName}) {
        printf("\n%-12s   %6d", $MailerName, $Mailers{$MailerName});
        $TotalNum += $Mailers{$MailerName};
        delete($Mailers{$MailerName});
     }
   }
   # now we print all remaining mailers
   foreach $MailerName (keys %Mailers) {
     printf("\n%-12s   %6d", $MailerName, $Mailers{$MailerName});
     $TotalNum += $Mailers{$MailerName};
   }
   printf("\n---------------------\nTOTAL:         %6d", $TotalNum);
   if ($RelayLocalhost > 0) {
      printf("\nin addition to %6d relay", $RelayLocalhost);
      print "\n     submission\(s\) from MSP";
   }
}

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

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

if ($NumTimeoutSendWarnings > 0) {
   print "\n\n" . $NumTimeoutSendWarnings . " warnings of delayed delivery after " . $TimeoutSendWarning;
}

if ($NumTimeoutSend > 0) {
   print "\n\n" . $NumTimeoutSend . " messages undelivered after " . $TimeoutSend;
}

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

if (keys %TLSFailed) {
   print "\n   and they failed because of:";
   foreach $TLSReason (keys %TLSFailed) {
      print "\n      $TLSReason";
   }
}

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

if ($TooManyRcpts > 0) {
   print "\n\n$TooManyRcpts messages with too many recipients";
}

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($XS4ALL > 0) {
   print "\n\n$XS4ALL messages discarded from XS4ALL";
}

if ($CantCreateOutput > 0) {
   print "\n\nCan't create output $CantCreateOutput Time(s)";
}

if ($OutdatedAliasdb > 0) {
   print "\n\nAliases database out of date $OutdatedAliasdb Time(s)";
}

if (keys %AttackAttempt) {
   print "\n\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 (keys %SaslError) {
   print "\n\nSASL database Errors:\n";
   foreach $File (sort {$a cmp $b} keys %SaslError) {
      print "   In file $File :\n";
      foreach $Error (sort {$a cmp $b} keys %{$SaslError{$File}}) {
         print "      $Error : $SaslError{$File}{$Error} Time(s)\n";
      }
   }
}

if (($Detail >= 10)) {
   print "\n\nMessage Size Distribution:\n";
   print "Range          # Msgs       KBytes\n";
   $TotalNum = 0;
   foreach $LastIndex (0..9) {
      $LastIndex2=9-$LastIndex;
      last if ($SizeDist[$LastIndex2]{'Bytes'} > 0);
   }

   foreach $ThisOne (0..$LastIndex2) {
      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 Messages (From \-\> To):\n";
   foreach $ThisOne (sort keys %LargeMsgs) {
      print "    $ThisOne : ${LargeMsgs{$ThisOne}} Time(s)\n";
   }
}

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

if (keys %LoadAvg) {
   print "\n\nWarning!!!:\n";
   print "Connections Rejected due to high load average $LoadAvgReject Time(s)\n";
   foreach $Load (sort keys %LoadAvg) {
      if ($Detail >=5) {
         print "    Load Avg $Load : $LoadAvg{$Load} Time(s)\n";
      }
      if ($Load > $MaxLoadAvg) {
         $MaxLoadAvg = $Load;
      }
   }
   print "   Max. Load Avg reached: $MaxLoadAvg\n";
}

if ($LoadAvgQueueSkip > 0) {
   print "\nAborted/skipped mail queue run - load average too high: $LoadAvgQueueSkip Time(s)\n";
}

if (keys %CollectError) {
   print "\n\nErrors during Collect:";
   foreach $Reason (sort keys %CollectError) {
      print "\n    $Reason";
      foreach $Source (%{$CollectError{$Reason}}) {
         print "\n    $Source: $CollectError{$Reason}{$Source} Time(s)";
      }
   }
}

if (keys %UnknownUsers) {
   foreach $Usr (sort keys %UnknownUsers) {
      foreach $QueueID (sort keys %{ $UnknownUsers{$Usr} }) {
         $SortedUsers{$Usr}{$Msgs{$QueueID}{"Relay"}}++;
         $ukusers++;
      }
      #@v = values %{$SortedUsers{$Usr}}; #What was this? -mgt
   }
   #Shuold $value of floor be displayed? -mgt
   if ($UnknownUsersThreshold) {
      print "\n\nUnknown local users: [Greater than $UnknownUsersThreshold emails]\n";
   } else {
      print "\n\nUnknown local users:\n";
   }
   foreach $Usr (sort keys %SortedUsers) {
      if ($Detail >= 10) {
         my $sort = CountOrder( %{$SortedUsers{$Usr}} );
         foreach $RelayHost (sort $sort keys %{ $SortedUsers{$Usr} }) {
            if ($SortedUsers{$Usr}{$RelayHost} >= $UnknownUsersThreshold) {
               print "  $Usr\n";
               print "       from $RelayHost    $SortedUsers{$Usr}{$RelayHost} time(s).\n";
            }
         }
      }
   }
   print "\n\t Total: $ukusers\n";
}

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

if (keys %UnknownUserscheckrcpt) {
   %SortedUsers = ();
   foreach $Usr (sort keys %UnknownUserscheckrcpt) {
      foreach $QueueID (sort keys %{ $UnknownUserscheckrcpt{$Usr} }) {
#         $SortedUsers{$Usr}{$Relays{$QueueID}}++;
#print "Adding to $Msgs{$QueueID}{'Relay'}\n";
         $SortedUsers{$Usr}{$Msgs{$QueueID}{"Relay"}}++;
      }
   }
   print "\n\nUnknown users:\n";
   foreach $Usr (keys %SortedUsers) {
      print "\n    $Usr\n";
      foreach $RelayHost (keys %{ $SortedUsers{$Usr} }) {
         print "      from $RelayHost    $SortedUsers{$Usr}{$RelayHost} Time(s).\n";
      }
   }
}

if (keys %DisabledMailbox) {
   %SortedUsers = ();
   foreach $Usr (sort keys %DisabledMailbox) {
      foreach $QueueID (sort keys %{ $DisabledMailbox{$Usr} }) {
#         $SortedUsers{$Usr}{$Relays{$QueueID}}++;
         $SortedUsers{$Usr}{$Msgs{$QueueID}{"Relay"}}++;
      }
   }
   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";
      }
   }
}

if (keys %QuotaExceed) {
   print "\n\nQuota exceeded for users:\n";
   foreach $User (sort {$a cmp $b} keys %QuotaExceed) {
      print "   $User : $QuotaExceed{$User} Time(s)\n";
   }
}

my $count = 0;
#Set up are defaults -mgt
my $MailbombListThreshold = 50;
if ( $ENV{'sendmail_mailbomblistthreshold'} ) { $MailbombListThreshold = "$ENV{'sendmail_mailbomblistthreshold'}"; };
my $MailbombThreshold = 10;
if ( $ENV{'sendmail_mailbombThreshold'} ) { $MailbombThreshold = "$ENV{'sendmail_mailbombthreshold'}"; };

foreach $ThisOne (sort {$MailBomber{$b}<=>$MailBomber{$a}} keys %MailBomber) {
   if ($MailBomber{$ThisOne} >= $MailbombThreshold and $count < $MailbombListThreshold) {
      print "\n\nTop relays (recipients/connections - min $MailbombThreshold  rcpts, max $MailbombListThreshold  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} Time(s)\n";
      $knspam = $knspam + $KnownSpammer{$ThisOne};
   }
   print "\n\tTotal:  $knspam\n";
}

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

if (keys %CheckMailReject) {
   print "\n\nRejected incoming mail:\n";
   foreach $ThisOne (keys %CheckMailReject) {
      print "    $ThisOne: $CheckMailReject{$ThisOne} Time(s)\n";
      $chkmreject = $chkmreject + $CheckMailReject{$ThisOne};
   }
   print "\n\tTotal:  $chkmreject\n";
}

if (keys %CheckRcptReject) {
   print "\n\nRejected mail:\n";
   foreach $ThisOne (keys %CheckRcptReject) {
      print "    $ThisOne: $CheckRcptReject{$ThisOne} Time(s)\n";
      $chkrereject = $chkrereject + $CheckRcptReject{$ThisOne};
   }
   print "\n\tTotal:  $chkrereject\n";
}

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

if (keys %MakeConnection) {
   print "\n\nConnections to other hosts failed:";
   foreach $Host (sort keys %MakeConnection) {
      print "\n    $Host:";
         foreach $Reason (keys %{$MakeConnection{$Host}}) {
            print "\n        $Reason: $MakeConnection{$Host}{$Reason} Time(s)";
         }
     }
}

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

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

if (keys %PREGreeting) {
   #Set up default -mgt
   my $PREGreetingThreshold = 1;
   if ( $ENV{'sendmail_pregreetingthreshold'} ) { $PREGreetingThreshold = $ENV{'sendmail_pregreetingthreshold'}; };
   print "\n\nGreet Pause Rejections:\n";
   foreach my $ip (sort {$a <=> $b} keys %PREGreeting) {
      if ($PREGreeting{$ip} >= $PREGreetingThreshold) {
         print "   From [$ip]: $PREGreeting{$ip} Time(s)\n";
      }
      $pregreetnum = $pregreetnum + $PREGreeting{$ip};
   }
   print "\n\tTotal:  $pregreetnum\n";
}

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

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

if (keys %BlackHoled) {
   print "\n\nBlackHole Totals:\n";
   foreach $ThisOne (sort keys %BlackHoles) {
      print "    $ThisOne: $BlackHoles{$ThisOne} Time(s)\n";
      $blktotal = $blktotal + $BlackHoles{$ThisOne};
   }
   if ($Detail >= 10) {
      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) {
#   foreach $ThisOne (keys %DomainErrors) {
      print "\n    From $ThisOne : " . keys(%{$DomainErrors{$ThisOne}}) . " Time(s)";
      foreach $User (keys %{$DomainErrors{$ThisOne}}) {
          print "\n        $User";
      }
      $domainer = $domainer + keys (%{$DomainErrors{$ThisOne}});
   }
   print "\n\n\tTotal:  $domainer\n";
}

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

if (keys %StatRejected) {
   print "\n\nMail Rejected:";
   foreach $Reason (sort keys %StatRejected) {
      print "\n    $Reason:";
      foreach $ToUser (keys %{$StatRejected{$Reason}}) {
          print "\n        To: $ToUser: $StatRejected{$Reason}{$ToUser} Time(s)";
      }
   }
}

if (keys %StatDeferred) {
   print "\n\nMail Deferred:";
   foreach $Reason (sort keys %StatDeferred) {
      print "\n    $Reason:";
      foreach $ToUser (keys %{$StatDeferred{$Reason}}) {
          print "\n        To: $ToUser: $StatDeferred{$Reason}{$ToUser} Time(s)";
      }
   }
}


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

if (keys %UnresolvedDomains) {
   #Set up default -mgt
   my $UnresolvedDomainsThreshold = 1;
   if ( $ENV{'sendmail_unresolveddomainsthreshold'} ) { $UnresolvedDomainsThreshold = $ENV{'sendmail_unresolveddomainsthreshold'}; };
   print "\n\nUnresolved sender domains:\n";
   my $count = CountOrder(%UnresolvedDomains);
   foreach $ThisOne (sort $count keys %UnresolvedDomains) {
      if ($UnresolvedDomains{$ThisOne} >= $UnresolvedDomainsThreshold) {
         print "    $ThisOne: $UnresolvedDomains{$ThisOne} Time(s)\n";
      }
      $ukndomain = $ukndomain + $UnresolvedDomains{$ThisOne};
   }
   print "\n\tTotal:  $ukndomain\n";
}

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

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

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 %StarttlsCert) {
  print "\n\nSTARTTLS failed to verify certificates because of the following reasons:\n";
  foreach $ThisOne (sort keys %StarttlsCert) {
      printf "    %s: %3i Time(s)\n" , $ThisOne , $StarttlsCert{$ThisOne};
   }
}

if (keys %CommandUnrecognized) {
   print "\n\nThe following were " . keys(%CommandUnrecognized) . " set(s) of unrecognized SMTP commands:\n";
   foreach $ThisOne (keys %CommandUnrecognized) {
      if (not defined $PREGreetingQueue{$QueueID}) {
         print "\n\tFrom QueueID: $ThisOne\n";
         print "$CommandUnrecognized{$ThisOne}";
      }
   }
}

foreach $StarttlsMode ('server', 'client') {
   if (($Starttls{$StarttlsMode}[0] + $Starttls{$StarttlsMode}[1] +
       $Starttls{$StarttlsMode}[2] + $Starttls{$StarttlsMode}[3] +
       $Starttls{$StarttlsMode}[4]) > 0) {
      print "\n\nFor STARTTLS in $StarttlsMode mode";
      if ($Starttls{$StarttlsMode}[0] > 0) {
         print ",\n\t$Starttls{$StarttlsMode}[0] requests were authenticated";
         }
      if ($Starttls{$StarttlsMode}[1] > 0) {
         print ",\n\t$Starttls{$StarttlsMode}[1] requests had temporary errors";
         }
      if ($Starttls{$StarttlsMode}[2] > 0) {
         print ",\n\t$Starttls{$StarttlsMode}[2] requests had SMTP errors";
         }
      if ($Starttls{$StarttlsMode}[3] > 0) {
         print ",\n\t$Starttls{$StarttlsMode}[3] requests failed handshake";
         }
      if ($Starttls{$StarttlsMode}[4] > 0) {
         print ",\n\t$Starttls{$StarttlsMode}[4] requests had unknown errors";
         }
      print ".\n";
      }
}

if (keys %StarttlsCipher) {
   print "\n\nSTARTTLS used the following encryption mechanisms";
   foreach $StarttlsCipherEntry (sort keys %StarttlsCipher) {
     print "\n   $StarttlsCipherEntry: $StarttlsCipher{$StarttlsCipherEntry} Time(s)";
   }
}

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/EXPN/ETRN (host,ruser):";
   foreach $host (sort keys %abuse) {
      print "\n    $host";
      $total = 0;
      foreach $luser (sort keys %{$abuse{$host}}) {
         print "\n       $luser:";
         foreach $RejCmd (sort keys %{$abuse{$host}{$luser}}) {
            print " " .  $RejCmd . (($abuse{$host}{$luser}{$RejCmd} > 1) ? " \(" . $abuse{$host}{$luser}{$RejCmd} . " Times\)" : " (1 Time)");
            $total+=$abuse{$host}{$luser}{$RejCmd};
         }
      }
      print "\n       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 %AddressError) {
   print "\n\nErrors in mail address:\n";
   foreach $Reason (sort {$a cmp $b} keys %AddressError) {
      print "   $Reason:\n";
      foreach $Address (sort {$a cmp $b} keys %{$AddressError{$Reason}}) {
         print "      $Address: $AddressError{$Reason}{$Address} Time(s)\n";
      }
   }
}

if (keys %SPFTotal) {
   print "\n\nSPF Milter Totals:\n";
   foreach my $type (sort {$a cmp $b} keys %SPFTotal) {
      print "   $type: $SPFTotal{$type} Time(s)\n";
   }
}

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

#Besure to add any newones to this total -mgt
$TotalRejected = $ukusers + $rldeny + $knspam + $blktotal + $domainer + $ukndomain + $uknhosts + $chkmreject + $chkrereject + $pregreetnum;
if ( $TotalRejected > 0 ) {
   print "\n\nSummary:\n";
   print "\tTotal Mail Rejected: $TotalRejected\n";
}

exit(0);

# vi: shiftwidth=3 tabstop=3 et


More information about the Logwatch-Devel mailing list