SF.net SVN: logwatch:[313] trunk/scripts/services/sendmail

[email protected] Thu, 11 Feb 2016 21:54:42 +0000
Newsgroups gmane.comp.log.logwatch.devel
Message-ID <[email protected]>
Revision: 313
          http://sourceforge.net/p/logwatch/code/313
Author:   bjorn1
Date:     2016-02-11 21:54:41 +0000 (Thu, 11 Feb 2016)
Log Message:
-----------
Rolled in changes previously in beta branch (PrettyPrint), and added better handling
of STARTTLS messages.

Modified Paths:
--------------
    trunk/scripts/services/sendmail

Modified: trunk/scripts/services/sendmail
===================================================================
--- trunk/scripts/services/sendmail	2016-02-11 00:48:15 UTC (rev 312)
+++ trunk/scripts/services/sendmail	2016-02-11 21:54:41 UTC (rev 313)
@@ -214,12 +214,14 @@
 #    etc, to [email protected]
 ########################################################
 
-#use diagnostics;
-#use strict;
+use diagnostics;
+use strict;
 
 use Logwatch ':sort';
 use Errno;
 
+# PrettyHost decomposes host names and IP addresses and
+# formats them to align vertically
 sub PrettyHost {
    # $_[0] is the line to format
    my $Line = $_[0];
@@ -230,7 +232,7 @@
        ($main::sendmail_prettyhost == 0)) {
       return($Line);
    }
-   my ($Name, $Addr, $Other) = ($Line =~ /^\s*(.*?)\s*(\[[\d.:]*\])\s*(.*?)\s*$/);
+   my ($Name, $Addr, $Other) = ($Line =~ /^\s*(.*?)\s*(\[[\d\.:]*\])\s*(.*?)\s*$/);
    if (index($Line, "\[") < 0) {
       $Name = $Line;
    }
@@ -243,13 +245,37 @@
    # From LineLength, we will use 18 chars for one space
    # and a full IPv4 address
 
-   while ((length($Name) > $LineLength-18) and (($Name =~ tr/\./\./) > 1) ) {
-      $Name =~ s/[^\.]*\.(.*)/$1/;
+   if (length($Line) > $LineLength) {
+      while ((length($Name) > $LineLength-21) and (($Name =~ tr/\./\./) > 1) ) {
+         $Name =~ s/[^\.]*\.(.*)/$1/;
+      }
+      $Name = "..." . $Name;
    }
    sprintf ("%*s %-17s", 18-$LineLength, $Name, $Addr);
 }
 
+# PrettyTimes simply formats the lines with counts "Time(s)" to
+# align with the results of the PrettyHost routine
 
+sub PrettyTimes {
+   # $_[0] is the event to format (string)
+   my $Line = $_[0];
+   # $_[1] is the number of times it occurs (integer)
+   my $Amount = $_[1];
+   #
+   if ((not defined $main::sendmail_prettyprint) or
+       ($main::sendmail_prettyprint == 0)) {
+      printf "\n%s: %d Time%s", $Line, $Amount, ($Amount == 1) ? "" : "s";
+   } else {
+      my $line_length = 72-length($Line);
+      printf "\n%s %*d Time%s", $Line, ($line_length > 0)? $line_length : 0,
+         $Amount, ($Amount == 1) ? "" : "s";
+   }
+   return 0;
+}
+
+
+
 my $LogwatchDetail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
 my $Debug = $ENV{'LOGWATCH_DEBUG'} || 0;
 
@@ -257,8 +283,14 @@
 my @MilterHeadersToCount = split(/\|/, $sendmail_milterheaderstocount);
 my $MatchFilter = $ENV{'sendmail_matchfilter'} || "";
 my $ReportFilter = $ENV{'sendmail_reportfilter'} || "";
-our $sendmail_prettyhost = $ENV{'sendmail_prettyhost'} || 0;
 
+# Extract formatting directives.  If Sendmail_PrettyHost is set,
+# assume that Sendmail_PrettyPrint is also desired
+our $sendmail_prettyhost = $ENV{'sendmail_prettyhost'};
+if (not defined $sendmail_prettyhost) {$sendmail_prettyhost = 1};
+our $sendmail_prettyprint = $ENV{'sendmail_prettyprint'};
+if ((not defined $sendmail_prettyprint) || $sendmail_prettyhost) {$sendmail_prettyprint = 1};
+
 my $Detail = $ENV{'sendmail_detail'};
 
 if (not defined $Detail) {
@@ -277,7 +309,7 @@
 my $DaemonThrottle =     my $LoadAvgQueueSkip =   my $LoadAvgReject =
 my $MsgsNoRcpt =
 my $MsgsSent =           my $NoMilterFilters =    my $NoMoreSpace =
-my $NumTimeoutSend =     my $NumTimeoutSendWarnings =my $OutdatedAliasdb =
+my $OutdatedAliasdb =
 my $OverSize =           my $OverSizeBytes =      my $RelayLocalhost =
 my $RemoteProtocolError =my $SendmailStarts =
 my $SendmailStopped =    my $TLSAcceptFailed =    my $TLSConnectFailed =
@@ -291,8 +323,8 @@
 my (
 $Address,                $Arg,                    $Attack,
 $Auth,
-$BlSite,                 $Bytes,                  $DeliverStat,
-$Dest,                   $Domain,
+$BlSite,                 $Bytes,                  $CommonName,
+$DeliverStat,            $Dest,                   $Domain,
 $Error,                  $ErrorCount,
 $ETRN,                   $File,                   $Forward,
 $FromUser,               $Header,                 $HeaderMod,
@@ -312,7 +344,7 @@
 $TimeoutSend,            $TimeoutSendWarning,     $TLSFile,
 $TLSReason,              $TotalBytes,             $TotalNum,
 $ToUser,                 $User,                   $Usr,
-$Warning,		 $Directory, 		  $Cause
+$Warning,                $Directory,              $Cause
 );
 
 
@@ -321,7 +353,7 @@
 
 my (
 %Abuse,                  %AddressError,           %AttackAttempt,
-%AuthWarns,              %BadAuth,
+%AUTHfailure,            %AuthWarns,              %BadAuth,
 %BadRcptThrottle,        %BlackHoled,
 %BlackHoles,             %CheckMailReject,        %CheckRcptReject,
 %CollectError,           %CommandUnrecognized,    %DisabledMailbox,
@@ -332,13 +364,13 @@
 %LoadAvg,                %LostInputChannel,
 %LostQueueFile,          %LowSpace,               %MailBomber,
 %MailBomberConn,         %Mailers,                %MailRejected,
-%MilterDeferrals,
+%MilterDeferrals,        %MilterErrors,
 %MilterHeaderCount,      %Msgs,                   %NotLocal,
 %OtherList,              %PREGreeting,            %PREGreetingQueue,
 %Quarantined,
 %RelayDenied,            %RelayReject,            %ReturnReceipts,
-%RuleSets,
-%SaslError,              %SenderIDresults,        %SortedUsers,
+%RuleSets,               %SaslError,              %SenderIDResults,
+%SentTimeouts,           %SortedUsers,
 %SPFResults,             %Starttls,               %StarttlsCert,
 %StarttlsCipher,         %StatDeferred,           %StatFileError,
 %StatRejected,           %StatRejectedLog,
@@ -348,12 +380,6 @@
 %WUnsafe
 );
 
-
-
-# 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;
@@ -460,6 +486,13 @@
       # Need RCPT most likely because of incorrect RCPT command, in which case ignore it
       ( ( $ThisLine =~ /^--- 503 5(\.[0-9]){2} Need RCPT \(recipient\)$/ ) and
           ( $Msgs{$QueueID}{"BadRCPT"} > 0)) or
+      ( $ThisLine =~ /^--- 530 5\.7\.0 Authentication required$/ ) or
+      # AUTH failure detected later with %AUTHfailure
+      ( $ThisLine =~ /^--- 535 5\.7\.0 authentication failed$/ ) or
+      # Mailbox disabled detected later by ruleset=check_rcpt
+      ( $ThisLine =~ /^--- 550 5(\.[0-9]){2} .* Mailbox disabled for this recipient$/ ) or
+      # bogus HELO detected later by rulteset=check_rcpt
+      ( $ThisLine =~ /^--- 550 5(\.[0-9]){2} .* bogus HELO name used/ ) or
       # Commands rejected are from greet_pause or milter
       ( $ThisLine =~ /^--- 550 5(\.[0-9]){2} Command rejected$/ ) or
       # User unknown detected later by ruleset=check_rcpt
@@ -467,7 +500,7 @@
       # Relaying denied detected later by ruleset=check_rcpt
       ( $ThisLine =~ /^--- 550 5(\.[0-9]){2} .*\.\.\. Relaying denied/ ) or
       # Access denied detected later by ruleset=check_relay
-      ( $ThisLine =~ /^--- 550 5(\.[0-9]){2} Access denied/ ) or
+      ( $ThisLine =~ /^--- 550 5(\.[0-9]){2} .*\.\.\. Access denied/ ) or
       # Domain errors detected later by ruleset=check_mail
       ( $ThisLine =~ /^--- 553 5(\.[0-9]){2} .*\.\.\. Domain of sender address .* does not exist$/ ) or
       ( $ThisLine =~ /^--- 553 5(\.[0-9]){2} .*\.\.\. Domain name required for sender address/ ) or
@@ -519,9 +552,22 @@
       ( $ThisLine =~ /^STARTTLS=client, start=ok$/ ) or
 
       # the following is described in tls.c as a bug in OpenSSL, and
-      # recommends that the error message be ignored (last checked on 8.13.3)
+      # recommends that the error message be ignored (last checked on 8.15.2)
       # file=tls.c, LogLevel>15, LOG_WARNING
       ( $ThisLine =~ /^STARTTLS=(server|client), SSL_shutdown not done$/ ) or
+      # and something similar occurs for the one sending the shutdown after the
+      # connection is already closed by the other side
+      # file=tls.c, LogLevel>11, LOG_WARNING
+      ( $ThisLine =~ /^STARTTLS=(server|client), SSL_shutdown failed/ ) or
+      # and similarly, an SSL write may fail because the remote host shuts down
+      # the connection first (ECONNRESET refers to "Connection reset by peer")
+      # file=sfsasl.c, LogLevel>8, LOG_WARNING
+      ( $ThisLine =~ /^STARTTLS: write error=syscall error \(-1\), errno=${\Errno::ECONNRESET}/ ) or
+      # file=srvsmtp.c, LogLevel>5, LOG_WARNING
+      ( $ThisLine =~ /^STARTTLS=server, error: accept failed=-1, reason=unknown, SSL_error=5, errno=${\Errno::ECONNRESET}, retry=/ ) or
+      # and yet another sympton of a connection shut down (EPIPE refers to "Broken pipe")
+      # file=srvsmtp.c, LogLevel>5, LOG_WARNING
+      ( $ThisLine =~ /^STARTTLS=server, error: accept failed=-1, reason=unknown, SSL_error=5, errno=${\Errno::EPIPE}, retry=/ ) or
       # the following is a log message introduced in 8.13.6
       # file=sfsasl.c, LogLevel>14, LOG_INFO
       # tls_retry errors are either transient, or additional log info is issued and parsed
@@ -645,12 +691,15 @@
    # file=deliver.c, LogLevel>-1, LOG_INFO
    } elsif ( ($ToUser, $MailerString, $DeliverStat) = ($ThisLine =~ /^to=(.*?), (.*)stat=(.*)/ ) ) {
       if ( $DeliverStat =~ /^Sent/ ) {
-     	   ( ($MailerType) = ( $MailerString =~ /mailer=(.*?),/));
-     	   ( ($RelayName) = ( $MailerString =~ /relay=(.*?),/));
+         ( ($MailerType) = ( $MailerString =~ /mailer=(.*?),/));
+         ( ($RelayName) = ( $MailerString =~ /relay=(.*?),/));
+         if (not defined $MailerType) {
+            $MailerType = "(unspecified)";
+         }
          $MailerType =~ s/^\s*$/\(unspecified\)/;
          # remove the entries from MSP (Mail Submission Program) relay to
          #    localhost
-         if (($MailerType =~ /^relay$/) and
+         if (($MailerType =~ /^relay$/) && (defined $RelayName) &&
                   ($RelayName =~ /\[127\.0\.0\.1\]/)) {
             $RelayLocalhost++;
          } else {
@@ -664,12 +713,14 @@
          $CleanTo =~ s/\>//g;
          $CleanTo =~ s/\"[\w\s]+\"\s?//g;
          $CleanTo =~ tr/A-Z/a-z/;
-         if (($CleanTo =~ m/\w+\@.+\,\w+/) && ($RelayName !~ m/\[127\.0\.0\.1\]/)) {
+         if (($CleanTo =~ m/\w+\@.+\,\w+/) && (defined $RelayName) &&
+               ($RelayName !~ m/\[127\.0\.0\.1\]/)) {
             my @CleanList = split(/,/, $CleanTo);
             for my $ListAddr (@CleanList) {
                $ToList{$ListAddr}++;
             }
-         } elsif  (($CleanTo =~ m/\w+\@[\w\.]+/) && ($RelayName !~ m/\[127\.0\.0\.1\]/)) {
+         } elsif  (($CleanTo =~ m/\w+\@[\w\.]+/) && (defined $RelayName) &&
+               ($RelayName !~ m/\[127\.0\.0\.1\]/)) {
             $ToList{$CleanTo}++;
          } elsif ($CleanTo =~ m/\w+/) {  # Match a simple name
             $ToList{$CleanTo}++;
@@ -699,8 +750,13 @@
       } elsif ($Reason =~ /^Unable to deliver mail$/) {
           $StatRejected{"Unable to deliver mail"}{"system notify"}++;
       # Return Receipts from successful delivery
-      } elsif ($Reason = ~/Return receipt$/) {
-	  $ReturnReceipts{$Msgs{$QueueID}{"FromUser"}}++;
+      } elsif ($Reason =~ /^Return receipt$/) {
+         $ReturnReceipts{$Msgs{$QueueID}{"FromUser"}}++;
+      # Timeouts
+      } elsif ($Reason =~ /^(Warning: could not send message for past .*)/ ) {
+          $SentTimeouts{$Reason}++;
+      } elsif ($Reason =~ /^(Cannot send message for .*)/ ) {
+          $SentTimeouts{$Reason}++;
       }
 
    # These are transient errors
@@ -713,15 +769,6 @@
    # file=deliver.c, LogLevel>4, LOG_INFO (versions 8.11 and earlier)
    } elsif ( ($NewQueueID, $Owner) = ( $ThisLine =~ /^clone ($QueueIDFormat), owner=(.*)/o ) ) {
       $Msgs{$NewQueueID}{"FromUser"} = $Owner;
-   # file=envelope.c
-   } elsif ( $ThisLine =~ /(return to sender|sender notify|postmaster notify|DSN): Warning: could not send message for past (.*)/ ) {
-      $TimeoutSendWarning = $2;
-      $NumTimeoutSendWarnings++;
-   } elsif ( $ThisLine =~ /(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/) {
-      $ReturnReceipts{$Msgs{$QueueID}{"FromUser"}}++;
    # file=main.c, LogLevel>-1, LOG_INFO
    } elsif ( $ThisLine =~ /^starting daemon/) {
       $SendmailStarts++;
@@ -790,18 +837,18 @@
       $Temp = "From " . $1 . " by " . $2;
       $BlackHoled{$Temp}++;
       $BlackHoles{$2}++;
-   } elsif ( ($Relay,$BlSite) = ($ThisLine =~ /^ruleset=(?:check_relay|check_rcpt), arg1=[^,]*,(?: arg2=[^,]*,)? relay=([^,]*), reject=55\d\s*[\d.]*\s*.*http:\/\/([^\/]*)\//) ) {
+   } elsif ( ($Relay,$BlSite) = ($ThisLine =~ /^ruleset=(?:check_relay|check_rcpt), arg1=[^,]*,(?: arg2=[^,]*,)? relay=([^,]*), reject=55\d\s*[\d.]*\s*.*http:\/\/([^\/\s]*)/) ) {
       $Temp = "From " . $Relay . " by " . $BlSite;
       $BlackHoled{$Temp}++;
       $BlackHoles{$BlSite}++;
-   } elsif ( ($Relay,$BlSite) = ($ThisLine =~ /^ruleset=(?:check_relay|check_rcpt), arg1=([^,]*),(?: arg2=[^,]*,)? reject=55\d\s*[\d.]*\s*.*http:\/\/([^\/]*)\//) ) {
+   } elsif ( ($Relay,$BlSite) = ($ThisLine =~ /^ruleset=(?:check_relay|check_rcpt), arg1=([^,]*),(?: arg2=[^,]*,)? reject=55\d\s*[\d.]*\s*.*http:\/\/([^\/\s]*)/) ) {
       #Example 553 error with NO RELAY -mgt
       #ruleset=check_relay, arg1=s010600402b39ee29.vf.shawcable.net, arg2=127.0.0.2, reject=553 5.3.0
       #Spam blocked see: http://spamcop.net/bl.shtml?70.68.8.182: 1 Time(s)
       $Temp = "From " . $Relay . " by " . $BlSite;
       $BlackHoled{$Temp}++;
       $BlackHoles{$BlSite}++;
-   } elsif ( ($Relay,$BlSite) = ($ThisLine =~ /reject=553\s*[\d.]*\s*<[^ ]*>\.\.\. +Mail from ([\d\.]+) rejected\;see http:\/\/([^\/]*)\//) ) {
+   } elsif ( ($Relay,$BlSite) = ($ThisLine =~ /reject=553\s*[\d.]*\s*<[^ ]*>\.\.\. +Mail from ([\d\.]+) rejected\;see http:\/\/([^\/\s]*)/) ) {
       #This is the another blackhole tag -mgt
       $Temp = "From " . $Relay . " by " . $BlSite;
       $BlackHoled{$Temp}++;
@@ -843,7 +890,7 @@
    } 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 (defined $OtherList{$Temp1}) {
          if ($OtherList{$Temp1} == 1) {
             delete ($OtherList{$Temp1});
          } else {
@@ -860,7 +907,7 @@
       }
    # similarly, delete last unmatched entry when too many bad commands
    } elsif ( $ThisLine =~ /^--- 421 4\.\d\.\d .* Too many bad commands; closing connection$/) {
-      if ($OtherList{$LastCmd{$QueueID}}) {
+      if (defined $OtherList{$LastCmd{$QueueID}}) {
          delete ($OtherList{$LastCmd{$QueueID}});
       }
 
@@ -870,7 +917,7 @@
    # file=srvrsmtp.c, LogLevel>5, LOG_INFO
    } elsif ( ( $Host ) = ($ThisLine =~ /(.*) (\(may be forged\) )?did not issue MAIL\/EXPN\/VRFY\/ETRN during connection to /) ) {
       # we test if they previously sent junk, because the connection is expected to fail
-      if ($CommandUnrecognized{$QueueID}) {
+      if (defined $CommandUnrecognized{$QueueID}) {
          $CommandUnrecognized{$QueueID} = $CommandUnrecognized{$QueueID} . "    ... and then exited without communicating\n";
       } else {
       $DummyConnection{$Host}++;
@@ -935,6 +982,9 @@
    # file=stats.c, LogLevel>12, LOG_INFO
    } elsif ( ($StatFile, $StatError) = ($ThisLine=~ /^poststats: (.*?): (.*)/) ) {
       $StatFileError{$StatFile}{$StatError}++;
+   # file=srvrsmtp.c, LogLevel>9, LOG_WARNING
+   } elsif ( ($Auth, $Reason, $RelayHost) = ($ThisLine =~ /^AUTH failure \((.*)?\): ([^\)]*)\(.* relay=(.*)/) ) {
+      $AUTHfailure{$RelayHost}{$Reason}++;
    # file=tls.c, LogLevel>7, LOG_INFO
    } elsif ($ThisLine=~ /STARTTLS=.* field=cn_issuer, status=failed to extract CN/ ) {
       $NoCommonName++;
@@ -954,23 +1004,18 @@
    } elsif ($ThisLine=~ /STARTTLS=client, error: connect failed=/) {
       $TLSConnectFailed++;
    # file=tls.c, LogLevel>-1, LOG_INFO
-   } elsif (($StarttlsReason) = ($ThisLine =~ /^STARTTLS: (?:x509|TLS) cert verify: depth=[0-9]+ .*, state=[0-9]+, reason=(.*)$/ )) {
-      $StarttlsCert{$StarttlsReason}++;
+   } elsif (($CommonName,$StarttlsReason) = ($ThisLine =~ /^STARTTLS: (?:x509|TLS) cert verify: depth=[0-9]+ .*\/CN=([^\/,]*).* state=[0-9]+, reason=(.*)$/ )) {
+      $StarttlsCert{$StarttlsReason}{$CommonName}++;
+   # do the same if, incorrectly, Common Name is not defined
+   } elsif (($StarttlsReason) = ($ThisLine =~ /^STARTTLS: (?:x509|TLS) cert verify: depth=[0-9]+ .* state=[0-9]+, reason=(.*)$/ )) {
+      $StarttlsCert{$StarttlsReason}{"(undefined CommonName)"}++;
    # 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]++;
+      if      ($StarttlsVerify =~ /^OK$|^TEMP$|^PROTOCOL$|^SOFTWARE$|^NO$|^NOT$|^FAIL$|^NONE$/) {
+         $Starttls{$StarttlsMode}{$StarttlsVerify}++;
       } else {
-         $Starttls{$StarttlsMode}[4]++;
+         $Starttls{$StarttlsMode}{'Other'}++;
       }
       $StarttlsCipher{"Cipher: " . $StarttlsCipherType . " Bits: " . $StarttlsNumBits}++;
    # file=queue.c, LogLevel>-1, LOG_ALERT
@@ -1038,12 +1083,12 @@
    } 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
+      # From:example.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
+#       From:example.com   REJECT
       $KnownSpammer{$1}++;
    # add support for DISCARD in /etc/mail/access
    } elsif ($ThisLine =~ /ruleset=check_(?:mail|rcpt), arg1=([^,]*), relay=.*\[.+\]( \(may be forged\))?, discard/) {
@@ -1089,7 +1134,7 @@
 
    } 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)
+      # From:example.com  ERROR:550 5.7.1 Relaying Denied (Spammer)
 
 # Note (-bl): this is the same as an earlier check_rcpt, except that the word Denied is capitalized here.
 #       So to avoid confusion I suggest that we use the REJECT label in the access file.
@@ -1134,12 +1179,23 @@
       chomp($ThisLine);
       # Report any unmatched entries...
       if ($ThisLine =~ /^<-- /) {
-         # sendmail converts non-ascii chars (remove high bit), so we do
-         # the same
-         $ThisLine =~ s/\\([23]\d{2})/chr(oct($1-200))/eg;
+         # sendmail converts some characters, so we do the same
+         $ThisLine =~ s/\\([23]\d{2})/
+            # clear the most significant bit if set
+            my $tempchar = oct($1 - 200);
+            # if the new value is a printable ASCII character, print it
+            if (($tempchar >= 32) && ($tempchar != 127)) {
+               chr($tempchar);
+            } else {
+            # if not printable ASCII, leave as octal code
+               sprintf("\\%o", $tempchar);
+            }/eg;
       }
-      # store last unmatched entry, in case it is needed later.
-      $LastCmd{$QueueID} = $ThisLine;
+      # store last unmatched entry, in case it is needed later.  But note that some
+      # statements have no QueueID.
+      if (defined $QueueID) {
+         $LastCmd{$QueueID} = $ThisLine;
+      }
       $OtherList{$ThisLine}++;
    }
 }
@@ -1174,7 +1230,7 @@
    # don't sort, as error order may help
    foreach $User (keys %SysErr) {
       foreach $Reason (keys %{$SysErr{$User}}) {
-         print "\n    $Reason: $SysErr{$User}{$Reason} Time(s)";
+         PrettyTimes("    $Reason", $SysErr{$User}{$Reason});
          $TotalSevereError[$SevereErrorIndex] += $SysErr{$User}{$Reason};
       }
    }
@@ -1248,20 +1304,21 @@
 
 if ($NoMilterFilters > 0) {
    eval "$PrintCond";
-   print "\n\nNo active milter filters\n";
+   print "\n\nNo active milter filters";
 }
 
 if ($OutdatedAliasdb > 0) {
    eval "$PrintCond";
-   print "\n\nAliases database out of date $OutdatedAliasdb Time(s)";
+   print "\n";
+   PrettyTimes("Aliases database out of date", $OutdatedAliasdb);
 }
 
 if (keys %WUnsafe) {
    print "\n\nUnsafe permissions:\n";
    foreach $Directory (keys %WUnsafe) {
-      print "   In program " . $Directory . ": \n";
+      print "   In program " . $Directory . ":";
       foreach $Cause (keys %{$WUnsafe{$Directory}}) {
-         print "      " . $Cause . ": " . $WUnsafe{$Directory}{$Cause} . " Time(s)\n";
+         PrettyTimes("      $Cause", $WUnsafe{$Directory}{$Cause});
       }
    }
 }
@@ -1270,18 +1327,18 @@
    eval "$PrintCond";
    print "\n\nSASL database Errors:\n";
    foreach $File (sort {$a cmp $b} keys %SaslError) {
-      print "   In file $File:\n";
+      print "   In file $File:";
       foreach $Error (sort {$a cmp $b} keys %{$SaslError{$File}}) {
-         print "      $Error: $SaslError{$File}{$Error} Time(s)\n";
+         PrettyTimes("      $Error", $SaslError{$File}{$Error});
       }
    }
 }
 
 if (keys %TooManyHops) {
    eval "$PrintCond";
-   print "\n\nToo many hops:\n";
+   print "\n\nToo many hops:";
    foreach $ThisOne (sort keys %TooManyHops) {
-      print "    $ThisOne: $TooManyHops{$ThisOne} Time(s)\n";
+      PrettyTimes("    $ThisOne", $TooManyHops{$ThisOne});
    }
 }
 
@@ -1296,14 +1353,14 @@
    if ($SendmailStarts > 0) {
       print "\n\nSendmail was started $SendmailStarts time(s)";
    }
-   print "\n\nMessages To Recipients:  $MsgsSent";
+   printf("\n\nMessages To Recipients:%11d", $MsgsSent);
    # Each explicitely addressed recipient in an email is counted as an
    # "Addressed Recipient"
-   print "\nAddressed Recipients:    $AddrRcpts";
-   print "\nBytes Transferred:       $BytesTransferred";
+   printf("\nAddressed Recipients:%13d", $AddrRcpts);
+   printf("\nBytes Transferred:%16d", $BytesTransferred);
    # Messages with no valid recipients (for example, fails
    # some other check) are not delivered
-   print "\nMessages No Valid Rcpts: $MsgsNoRcpt";
+   printf("\nMessages No Valid Rcpts:%10d", $MsgsNoRcpt);
 }
 
 
@@ -1339,7 +1396,7 @@
    eval "$PrintCond";
    print "\n\nLarge Messages (From \-\> To):";
    foreach $ThisOne (sort keys %LargeMsgs) {
-      print "\n    $ThisOne: ${LargeMsgs{$ThisOne}} Time(s)";
+      PrettyTimes("    $ThisOne", ${LargeMsgs{$ThisOne}});
    }
 }
 
@@ -1380,10 +1437,9 @@
    #Set sendmail_tolistthreshold = Null to suppress this report -mgt
    if ($ToListThreshold !~ m/NULL/i) {
       print "\n\nTop $ToListThreshold Email Recipients\n";
-      print "----------------------------------\n";
       foreach my $ToAddr (sort {$ToList{$b}<=>$ToList{$a}} keys %ToList) {
          if ($ToListCount >= $ToListThreshold) { last; };
-         print "$ToAddr : $ToList{$ToAddr} emails\n";
+         PrettyTimes("    " . $ToAddr, $ToList{$ToAddr});
          $ToListCount++;
       }
    }
@@ -1398,8 +1454,10 @@
 
    foreach $ThisOne (sort {$MailBomber{$b}<=>$MailBomber{$a}} keys %MailBomber) {
       if ($MailBomber{$ThisOne} >= $MailbombThreshold and $MailBombCount < $MailbombListThreshold) {
-         print "\n\nTop relays (recipients/connections - min $MailbombThreshold  rcpts, max $MailbombListThreshold  lines):" if ! $MailBombCount;
-         print "\n    $MailBomber{$ThisOne}/$MailBomberConn{$ThisOne}: $ThisOne";
+         print "\n\nTop relays (recipients / connections - min $MailbombThreshold rcpts, max $MailbombListThreshold lines):"
+            if ! $MailBombCount;
+         printf("\n    %s%5d / %4d", PrettyHost($ThisOne, 63), $MailBomber{$ThisOne},
+            $MailBomberConn{$ThisOne});
       }
       $MailBombCount++;
    }
@@ -1411,7 +1469,7 @@
    print "Connections Rejected due to high load average $LoadAvgReject Time(s)";
    my $MaxLoadAvg = 0;
    foreach $Load (sort keys %LoadAvg) {
-      print "\n    Load Avg $Load: $LoadAvg{$Load} Time(s)";
+      PrettyTimes("    Load Avg $Load", $LoadAvg{$Load});
       if ($Load > $MaxLoadAvg) {
          $MaxLoadAvg = $Load;
       }
@@ -1421,45 +1479,67 @@
 
 if (($Detail >= 5) and ($LoadAvgQueueSkip > 0)) {
    eval "$PrintCond";
-   print "\n\nAborted/skipped mail queue run - load average too high: $LoadAvgQueueSkip Time(s)";
+   print "\n";
+   PrettyTimes("Aborted/skipped mail queue run - load average too high", $LoadAvgQueueSkip);
 }
 
 if ($Detail >= 10) {
    foreach $StarttlsMode ('server', 'client') {
-      if (($Starttls{$StarttlsMode}[0] + $Starttls{$StarttlsMode}[1] +
-         $Starttls{$StarttlsMode}[2] + $Starttls{$StarttlsMode}[3] +
-         $Starttls{$StarttlsMode}[4]) > 0) {
+      if (keys %{$Starttls{$StarttlsMode}}) {
          eval "$PrintCond";
 
-         print "\n\nFor STARTTLS in $StarttlsMode mode";
-         if ($Starttls{$StarttlsMode}[0] > 0) {
-            print ",\n\t$Starttls{$StarttlsMode}[0] requests were authenticated";
+         print "\n\nFor STARTTLS in $StarttlsMode mode,";
+         if (defined $Starttls{$StarttlsMode}{'OK'}) {
+            PrettyTimes("    Requests were authenticated",
+               $Starttls{$StarttlsMode}{'OK'});
             }
-         if ($Starttls{$StarttlsMode}[1] > 0) {
-            print ",\n\t$Starttls{$StarttlsMode}[1] requests had temporary errors";
+         if (defined $Starttls{$StarttlsMode}{'TEMP'}) {
+            PrettyTimes("    Requests had temporary errors",
+               $Starttls{$StarttlsMode}{'TEMP'});
             }
-         if ($Starttls{$StarttlsMode}[2] > 0) {
-            print ",\n\t$Starttls{$StarttlsMode}[2] requests had SMTP errors";
+         if (defined $Starttls{$StarttlsMode}{'PROTOCOL'}) {
+            PrettyTimes("    Requests had SMTP protocol errors",
+               $Starttls{$StarttlsMode}{'PROTOCOL'});
             }
-         if ($Starttls{$StarttlsMode}[3] > 0) {
-            print ",\n\t$Starttls{$StarttlsMode}[3] requests failed handshake";
+         if (defined $Starttls{$StarttlsMode}{'SOFTWARE'}) {
+            PrettyTimes("    Requests failed STARTTLS handshake",
+               $Starttls{$StarttlsMode}{'SOFTWARE'});
             }
-         if ($Starttls{$StarttlsMode}[4] > 0) {
-            print ",\n\t$Starttls{$StarttlsMode}[4] requests had unknown errors";
+         if (defined $Starttls{$StarttlsMode}{'NO'}) {
+            PrettyTimes("    No cert was presented",
+               $Starttls{$StarttlsMode}{'NO'});
             }
+         if (defined $Starttls{$StarttlsMode}{'NOT'}) {
+            PrettyTimes("    No cert was requested",
+               $Starttls{$StarttlsMode}{'NOT'});
+            }
+         if (defined $Starttls{$StarttlsMode}{'FAIL'}) {
+            PrettyTimes("    Cert was presented, but not verified",
+               $Starttls{$StarttlsMode}{'FAIL'});
+            }
+         if (defined $Starttls{$StarttlsMode}{'NONE'}) {
+            PrettyTimes("    STARTTLS was not performed",
+               $Starttls{$StarttlsMode}{'NONE'});
+            }
+         if (defined $Starttls{$StarttlsMode}{'Other'}) {
+            PrettyTimes("    Requests had unknown errors",
+               $Starttls{$StarttlsMode}{'Other'});
+            }
          }
    }
-   if (keys %StarttlsCipher) {
+   if (defined keys %StarttlsCipher) {
       eval "$PrintCond";
       print "\n\nSTARTTLS used the following encryption mechanisms";
       foreach $StarttlsCipherEntry (sort keys %StarttlsCipher) {
-        print "\n   $StarttlsCipherEntry: $StarttlsCipher{$StarttlsCipherEntry} Time(s)";
+         PrettyTimes("    " . $StarttlsCipherEntry, 
+            $StarttlsCipher{$StarttlsCipherEntry});
       }
    }
    if ($NoCommonName) {
       eval "$PrintCond";
       # The following is a frequent occurrence, but not an error
-      print "\n\nFor STARTTLS, no CommonName given $NoCommonName time(s).";
+      print "\n";
+      PrettyTimes("For STARTTLS, no CommonName given", $NoCommonName);
       }
 }
 
@@ -1467,7 +1547,7 @@
    eval "$PrintCond";
    print "\n\nETRNs Received:";
    foreach $ThisOne (sort keys %ETRNs) {
-      print "\n    $ThisOne: $ETRNs{$ThisOne} Time(s)";
+      PrettyTimes("    $ThisOne", $ETRNs{$ThisOne});
    }
 }
 
@@ -1475,7 +1555,7 @@
    eval "$PrintCond";
    print "\n\nSuccessful Return Receipts:";
    foreach $ThisOne (sort keys %ReturnReceipts) {
-      print "\n    $ThisOne: $ReturnReceipts{$ThisOne} Time(s)";
+      PrettyTimes("    $ThisOne", $ReturnReceipts{$ThisOne});
    }
 }
 
@@ -1483,7 +1563,7 @@
    eval "$PrintCond";
    print "\n\nHeaders modified by Milter:";
    foreach $Header (sort keys %MilterHeaderCount) {
-      print "\n    $Header : $MilterHeaderCount{$Header}";
+      PrettyTimes( "    $Header", $MilterHeaderCount{$Header});
    }
 }
 
@@ -1491,7 +1571,7 @@
    eval "$PrintCond";
    print "\n\nMilter transient failures:";
    foreach $Reason (sort keys %MilterDeferrals) {
-      print "\n    $Reason: $MilterDeferrals{$Reason} Time(s)";
+      PrettyTimes( "    $Reason", $MilterDeferrals{$Reason});
    }
 }
 
@@ -1499,7 +1579,7 @@
    print "\n\nDNS Map lookups:";
    foreach $Domain (sort keys %DNSMap) {
       foreach $IP (sort keys %{$DNSMap{$Domain}}) {
-         print "\n   $Domain => $IP : $DNSMap{$Domain}{$IP} Time(s)";
+         PrettyTimes( "   $Domain => $IP", $DNSMap{$Domain}{$IP});
       }
    }
 }
@@ -1507,14 +1587,14 @@
 if (($Detail >= 10) and (keys %SenderIDResults)) {
    print "\n\nSender-ID Results:";
    foreach my $SenderIDStatus (sort keys %SenderIDResults) {
-      printf ("\n   %25s:%6s Time(s)", $SenderIDStatus, $SenderIDResults{$SenderIDStatus});
+      PrettyTimes("   $SenderIDStatus", $SenderIDResults{$SenderIDStatus});
    }
 }
 
 if (($Detail >= 10) and (keys %SPFResults)) {
    print "\n\nSPF Results:";
    foreach my $SPFStatus (sort keys %SPFResults) {
-      printf ("\n   %25s:%6s Time(s)", $SPFStatus, $SPFResults{$SPFStatus});
+      PrettyTimes("   $SPFStatus", $SPFResults{$SPFStatus});
    }
 }
 $TotalHeaderPrinted += $HeaderPrinted;
@@ -1557,12 +1637,27 @@
    eval "$PrintCond" if ($Detail >= 3);
    print "\n\nBad AUTH mechanism requests" if ($Detail >= 3);
    foreach $Auth (sort keys %BadAuth) {
-      print "\n    $Auth: $BadAuth{$Auth} Time(s)" if ($Detail >= 5);
+      PrettyTimes("    " . $Auth, $BadAuth{$Auth}) if ($Detail >= 5);
       $TotalError[$ErrorIndex] += $BadAuth{$Auth};
    }
+   print "\n\tTotal:  $TotalError[$ErrorIndex]" if( $Detail >=3 );
 }
 $TotalError[++$ErrorIndex] = 0;
 
+if (keys %AUTHfailure) {
+   eval "$PrintCond" if ($Detail >= 3);
+   print "\n\nFailed AUTH requests" if ($Detail >= 3);
+   foreach $Host (sort keys %AUTHfailure) {
+      print "\n    From " . PrettyHost($Host, 58) if ($Detail >=5);
+      foreach $Auth (sort keys %{$AUTHfailure{$Host}}) {
+         PrettyTimes("        $Auth", $AUTHfailure{$Host}{$Auth}) if ($Detail >= 5);
+         $TotalError[$ErrorIndex] += $AUTHfailure{$Host}{$Auth};
+      }
+   }
+   print "\n\tTotal:  $TotalError[$ErrorIndex]" if ($Detail >= 3);
+}
+$TotalError[++$ErrorIndex] = 0;
+
 if($RemoteProtocolError > 0) {
    eval "$PrintCond" if ($Detail >= 3);
    print "\n\n" . $RemoteProtocolError . " Remote Protocol Errors" if ($Detail >= 3);
@@ -1576,11 +1671,11 @@
    foreach $Host (sort {$a cmp $b} keys %AttackAttempt) {
       print "\n   Attempt from $Host with:";
       foreach $Attack (sort {$a cmp $b} keys %{$AttackAttempt{$Host}}) {
-         print "\n      $Attack: $AttackAttempt{$Host}{$Attack} Time(s)";
+         PrettyTimes("      $Attack", $AttackAttempt{$Host}{$Attack});
          $TotalError[$ErrorIndex] += $AttackAttempt{$Host}{$Attack};
       }
    }
-   print "\n\tTotal:  $TotalError[$ErrorIndex] Time(s)";
+   print "\n\tTotal:  $TotalError[$ErrorIndex]";
 }
 $TotalError[++$ErrorIndex] = 0;
 
@@ -1592,7 +1687,7 @@
    print "\n\nMail attempts from known spammers: [Occurrences >= $KnownSpammerThreshold]" if ($Detail >= 3);
    foreach $ThisOne (sort $KnownSpammerCount keys %KnownSpammer) {
       if ($KnownSpammer{$ThisOne} >= $KnownSpammerThreshold) {
-         printf("\n    %s %3i Time(s)", PrettyHost($ThisOne, 63), $KnownSpammer{$ThisOne}) if ($Detail >= 5);
+         PrettyTimes("    " . PrettyHost($ThisOne, 63), $KnownSpammer{$ThisOne}) if ($Detail >= 5);
       }
       $TotalError[$ErrorIndex] += $KnownSpammer{$ThisOne};
    }
@@ -1617,7 +1712,8 @@
       if ($RelayDeniedCount >= $RelayDeniedThreshold)  {
          printf("\n    From %s", PrettyHost($Relay, 58)) if ($Detail >=5);
          foreach $Dest (keys %{$RelayDenied{$Relay}}) {
-            print "\n        To $Dest: $RelayDenied{$Relay}{$Dest} Time(s)" if ($Detail >= 5);
+            PrettyTimes("        to " . $Dest,
+               $RelayDenied{$Relay}{$Dest}) if ($Detail >= 5);
          }
       }
    }
@@ -1633,7 +1729,7 @@
    print "\n\nRejected incoming mail: [Occurrences >= $CheckMailRejectThreshold]" if ($Detail >= 3);
    foreach $ThisOne (keys %CheckMailReject) {
       if ($CheckMailReject{$ThisOne} >= $CheckMailRejectThreshold)  {
-         print "\n    $ThisOne: $CheckMailReject{$ThisOne} Time(s)" if ($Detail >= 5);
+         PrettyTimes("    $ThisOne", $CheckMailReject{$ThisOne}) if ($Detail >= 5);
       }
       $TotalError[$ErrorIndex] += $CheckMailReject{$ThisOne};
    }
@@ -1649,7 +1745,7 @@
 
    print "\n\nGreet Pause Rejections: [Occurrences >= $PREGreetingThreshold]" if ($Detail >= 3);
    foreach my $ip (sort $PREGreetingCount keys %PREGreeting) {
-      printf("\n    From %s %3i Time(s)", PrettyHost($ip, 58), $PREGreeting{$ip}) if ($Detail >= 5)
+      PrettyTimes("    " . PrettyHost($ip, 63), $PREGreeting{$ip}) if ($Detail >= 5)
        && ($PREGreeting{$ip} >= $PREGreetingThreshold);
       $TotalError[$ErrorIndex] += $PREGreeting{$ip};
    }
@@ -1666,7 +1762,7 @@
    print "\n\nLost input channel: [Occurrences >= $LostInputChannelThreshold]" if ($Detail >= 3);
    foreach $ThisOne (sort $LostInputChannelCount keys %LostInputChannel) {
       if ($LostInputChannel{$ThisOne} >= $LostInputChannelThreshold)  {
-         printf("\n    %s %3i Time(s)", PrettyHost($ThisOne, 63), $LostInputChannel{$ThisOne}) if ($Detail >= 5);
+         PrettyTimes("    " . PrettyHost($ThisOne, 63), $LostInputChannel{$ThisOne}) if ($Detail >= 5);
       }
       $TotalError[$ErrorIndex] += $LostInputChannel{$ThisOne};
    }
@@ -1683,7 +1779,7 @@
    print "\n\nClient quit before communicating: [Occurrences >= $DummyConnectionThreshold]" if ($Detail >= 3);
    foreach $ThisOne (sort $DummyConnectionCount keys %DummyConnection) {
       if ($DummyConnection{$ThisOne} >= $DummyConnectionThreshold)  {
-         printf("\n    %s %3i Time(s)", PrettyHost($ThisOne, 63), $DummyConnection{$ThisOne}) if ($Detail >= 5);
+         PrettyTimes("    " . PrettyHost($ThisOne, 63), $DummyConnection{$ThisOne}) if ($Detail >= 5);
       }
       $TotalError[$ErrorIndex] += $DummyConnection{$ThisOne};
    }
@@ -1703,12 +1799,13 @@
       my $sublist;
       my $subcounter = 0;
       foreach $User (sort $subcount keys %{$DomainErrors{$ThisOne}}) {
-         $sublist .= "\n        $User : $DomainErrors{$ThisOne}{$User} Time(s)" if ($Detail >= 10);
          $subcounter += $DomainErrors{$ThisOne}{$User};
       }
       if ( $subcounter >= $DomainErrorsThreshold) {
-         printf("\n    From %s %3i Time(s)", PrettyHost($ThisOne, 58), $subcounter) if ($Detail >= 5);
-         print $sublist;
+         PrettyTimes("    From " . PrettyHost($ThisOne, 58), $subcounter) if ($Detail >=5);
+         foreach $User (sort $subcount keys %{$DomainErrors{$ThisOne}}) {
+            PrettyTimes("        $User", $DomainErrors{$ThisOne}{$User}) if ($Detail >= 10);
+         }
       }
       $TotalError[$ErrorIndex] += $subcounter;
    }
@@ -1720,7 +1817,7 @@
    eval "$PrintCond" if ($Detail >= 3);
    print "\n\nAuthentication warnings:" if ($Detail >= 3);
    foreach $ThisOne (sort keys %AuthWarns) {
-      print "\n    $ThisOne: $AuthWarns{$ThisOne} Time(s)" if ($Detail >= 5);;
+      PrettyTimes("    $ThisOne", $AuthWarns{$ThisOne}) if ($Detail >= 5);;
       $TotalError[$ErrorIndex] += $AuthWarns{$ThisOne};
    }
    print "\n\tTotal:  $TotalError[$ErrorIndex]" if ($Detail >= 3);
@@ -1735,7 +1832,8 @@
    print "\n\nTimeouts:  [Occurrences >= $TimeoutThreshold]" if ($Detail >= 3);
    my $TimeoutCount = CountOrder(%Timeouts);
    foreach $ThisOne (sort $TimeoutCount keys %Timeouts) {
-      print "\n    $ThisOne: $Timeouts{$ThisOne} Time(s)" if (($Detail >= 5) && ( $Timeouts{$ThisOne} >= $TimeoutThreshold));
+      PrettyTimes("    $ThisOne", $Timeouts{$ThisOne}) if (($Detail >= 5) &&
+         ($Timeouts{$ThisOne} >= $TimeoutThreshold));
       $TotalError[$ErrorIndex] += $Timeouts{$ThisOne};
    }
    print "\n\tTotal:  $TotalError[$ErrorIndex]" if ($Detail >= 3);
@@ -1753,7 +1851,7 @@
       foreach $Luser (sort keys %{$Abuse{$Host}}) {
          print "\n        $Luser:" if ($Detail >= 5);
          foreach $RejCmd (sort keys %{$Abuse{$Host}{$Luser}}) {
-            print "  $RejCmd: $Abuse{$Host}{$Luser}{$RejCmd} Time(s)" if ($Detail >= 5);
+            PrettyTimes("  $RejCmd", $Abuse{$Host}{$Luser}{$RejCmd}) if ($Detail >= 5);
             $TotalAbuse += $Abuse{$Host}{$Luser}{$RejCmd};
          }
       }
@@ -1780,8 +1878,12 @@
    eval "$PrintCond" if ($Detail >= 3);
    print "\n\nSTARTTLS failed to verify certificates:" if ($Detail >= 3);
    foreach $ThisOne (sort keys %StarttlsCert) {
-      printf "\n    %s: %i Time(s)" , $ThisOne , $StarttlsCert{$ThisOne} if ($Detail >= 5);
-      $TotalError[$ErrorIndex] += $StarttlsCert{$ThisOne};
+      printf "\n    %s:", $ThisOne if ($Detail >= 5);
+      foreach $CommonName (sort keys %{$StarttlsCert{$ThisOne}}) {
+         PrettyTimes("       CN: $CommonName", $StarttlsCert{$ThisOne}{$CommonName})
+            if ($Detail >= 5);
+         $TotalError[$ErrorIndex] += $StarttlsCert{$ThisOne}{$CommonName};
+      }
    }
    print "\n\tTotal:  $TotalError[$ErrorIndex]" if ($Detail >= 3);
 }
@@ -1809,14 +1911,13 @@
       my $colerror = 0;
       foreach $Source (keys %{$CollectError{$Reason}}) {
          if ($CollectError{$Reason}{$Source} >= $CollectErrorThreshold) {
-            print "\n      $Source : $CollectError{$Reason}{$Source} Time(s)" if ($Detail >= 5);
+            PrettyTimes("      $Source", $CollectError{$Reason}{$Source}) if ($Detail >= 5);
          }
          $colerror = $colerror + $CollectError{$Reason}{$Source};
       }
-      if ($Detail >= 5)    { print "\n    Total: $colerror"; }
-      elsif ($Detail >= 3) { print "\n    $Reason: $colerror"; }
       $TotalError[$ErrorIndex] += $colerror;
    }
+   print "\n\tTotal: $TotalError[$ErrorIndex]" if ($Detail >= 3);
 }
 $TotalError[++$ErrorIndex] = 0;
 
@@ -1827,7 +1928,8 @@
 
    print "\n\nClient submitted too many bad recipients: [Occurrences >= $BadRcptThrottleThreshold]" if ($Detail >= 3);
    foreach $ThisOne (sort $BadRcptCount keys %BadRcptThrottle) {
-      print "\n    $ThisOne: $BadRcptThrottle{$ThisOne} Time(s)" if ($Detail >= 5)
+      PrettyTimes("    " . PrettyHost($ThisOne,61),
+         $BadRcptThrottle{$ThisOne}) if ($Detail >= 5)
          && ( $BadRcptThrottle{$ThisOne} >= $BadRcptThrottleThreshold );
       $TotalError[$ErrorIndex] += $BadRcptThrottle{$ThisOne};
    }
@@ -1846,7 +1948,7 @@
    eval "$PrintCond" if ($Detail >= 3);
    print "\n\nToo large headers from:" if ($Detail >= 3);
    foreach $Host ( sort {$LargeHdrs{$b}<=>$LargeHdrs{$a}} keys %LargeHdrs ) {
-      printf "\n    %-17s   %-3i Time(s)",$Host, $LargeHdrs{$Host} if ($Detail >= 5);
+      PrettyTimes("    $Host", $LargeHdrs{$Host}) if ($Detail >= 5);
       $TotalError[$ErrorIndex] += $LargeHdrs{$Host};
    }
    print "\n\tTotal:  $TotalError[$ErrorIndex]" if ($Detail >= 3);
@@ -1859,7 +1961,7 @@
    foreach $Reason (sort {$a cmp $b} keys %AddressError) {
       print "\n   $Reason:" if ($Detail >= 5);
       foreach $Address (sort {$a cmp $b} keys %{$AddressError{$Reason}}) {
-         print "\n      $Address: $AddressError{$Reason}{$Address} Time(s)" if ($Detail >= 5);
+         PrettyTimes("      $Address", $AddressError{$Reason}{$Address}) if ($Detail >= 5);
       $TotalError[$ErrorIndex] += $AddressError{$Reason}{$Address};
       }
    }
@@ -1875,7 +1977,7 @@
    print "\n\nMessages quarantined by milter: [Occurrences >= $QuarantinedThreshold]" if ($Detail >= 3);
    foreach $ThisOne (sort $QuarantinedCount keys %Quarantined) {
       if ($Quarantined{$ThisOne} >= $QuarantinedThreshold)  {
-         printf("\n    %s: %3i Time(s)", $ThisOne, $Quarantined{$ThisOne}) if ($Detail >= 5);
+         PrettyTimes("    $ThisOne", $Quarantined{$ThisOne}) if ($Detail >= 5);
       }
       $TotalError[$ErrorIndex] += $Quarantined{$ThisOne};
    }
@@ -1883,24 +1985,18 @@
 }
 $TotalError[++$ErrorIndex] = 0;
 
-
-
 # Recipient errors
-
-if ($NumTimeoutSendWarnings > 0) {
+if (keys %SentTimeouts) {
    eval "$PrintCond" if ($Detail >= 3);
-   print "\n\n" . $NumTimeoutSendWarnings . " warnings of delayed delivery after " . $TimeoutSendWarning if ($Detail >= 3);
-   $TotalError[$ErrorIndex] += $NumTimeoutSendWarnings;
+   print "\n\nMessages delayed by recipients:" if ($Detail >= 3);
+   foreach $ThisOne (keys %SentTimeouts) {
+     PrettyTimes("    $ThisOne", $SentTimeouts{$ThisOne}) if ($Detail >=5); 
+     $TotalError[$ErrorIndex] += $SentTimeouts{$ThisOne};
+   }
+   print "\n\tTotal:  $TotalError[$ErrorIndex]" if ($Detail >= 3);
 }
 $TotalError[++$ErrorIndex] = 0;
 
-if ($NumTimeoutSend > 0) {
-   eval "$PrintCond" if ($Detail >= 3);
-   print "\n\n" . $NumTimeoutSend . " messages undelivered after " . $TimeoutSend if ($Detail >= 3);
-   $TotalError[$ErrorIndex] += $NumTimeoutSend;
-}
-$TotalError[++$ErrorIndex] = 0;
-
 if (keys %UnknownUsers) {
    eval "$PrintCond" if ($Detail >= 3);
    %SortedUsers = ();
@@ -1927,10 +2023,11 @@
             $UnknownUsersCount += $SortedUsers{$Usr}{$RelayHost};
          }
          if ($UnknownUsersCount >= $UnknownUsersThreshold) {
-            print "\n    $Usr : $UnknownUsersCount Time(s)";
+            PrettyTimes("    " . $Usr, $UnknownUsersCount);
             if ($Detail >= 15) {
                foreach $RelayHost (sort $subcount keys %{ $SortedUsers{$Usr} }) {
-                  printf ("\n        from %s %3i Time(s)", PrettyHost($RelayHost, 54), $SortedUsers{$Usr}{$RelayHost});
+                  PrettyTimes("        from " . PrettyHost($RelayHost, 54),
+                     $SortedUsers{$Usr}{$RelayHost});
                }
             }
          }
@@ -1957,7 +2054,7 @@
       print "\n\nUnknown users:" if ($Detail >= 3);
    }
    if ($Detail >= 5) {
-       my $count = TotalCountOrder( %SortedUsers );
+      my $count = TotalCountOrder( %SortedUsers );
 
       foreach $Usr (sort $count keys %SortedUsers) {
          my $subcount = CountOrder( %{$SortedUsers{$Usr}} );
@@ -1966,10 +2063,11 @@
             $UnknownUsersCount += $SortedUsers{$Usr}{$RelayHost};
          }
          if ($UnknownUsersCount >= $UnknownUsersThreshold) {
-            print "\n    $Usr : $UnknownUsersCount Time(s)";
+            PrettyTimes("    " . $Usr, $UnknownUsersCount);
             if ($Detail >= 15) {
                foreach $RelayHost (sort $subcount keys %{ $SortedUsers{$Usr} }) {
-                  printf ("\n        from %s %3i Time(s)", PrettyHost($RelayHost, 54), $SortedUsers{$Usr}{$RelayHost});
+                  PrettyTimes("        from " . PrettyHost($RelayHost, 54),
+                     $SortedUsers{$Usr}{$RelayHost});
                }
             }
          }
@@ -1992,7 +2090,8 @@
    foreach $Usr (sort keys %SortedUsers) {
       print "\n    $Usr" if ($Detail >= 5);
       foreach $RelayHost (sort keys %{ $SortedUsers{$Usr} }) {
-         printf("\n      from %s %3i Time(s)", PrettyHost($RelayHost, 54), $SortedUsers{$Usr}{$RelayHost}) if ($Detail >= 5);
+         PrettyTimes("      from " . PrettyHost($RelayHost, 54),
+            $SortedUsers{$Usr}{$RelayHost}) if ($Detail >= 5);
       }
    }
    print "\n\tTotal:  $TotalError[$ErrorIndex]" if ($Detail >= 3);
@@ -2007,7 +2106,7 @@
    print "\n\nRejected mail: [Occurrences >= $CheckRcptRejectThreshold]" if ($Detail >= 3);
    foreach $ThisOne (keys %CheckRcptReject) {
       if ($CheckRcptReject{$ThisOne} >= $CheckRcptRejectThreshold)  {
-         print "\n    $ThisOne: $CheckRcptReject{$ThisOne} Time(s)" if ($Detail >= 5);
+         PrettyTimes("    $ThisOne", $CheckRcptReject{$ThisOne}) if ($Detail >= 5);
       }
       $TotalError[$ErrorIndex] += $CheckRcptReject{$ThisOne};
    }
@@ -2021,7 +2120,7 @@
    foreach $Reason (sort keys %StatRejected) {
       print "\n    $Reason:" if ($Detail >= 5);
       foreach $ToUser (keys %{$StatRejected{$Reason}}) {
-          print "\n        To: $ToUser: $StatRejected{$Reason}{$ToUser} Time(s)" if ($Detail >= 5);
+          PrettyTimes("        " . $ToUser, $StatRejected{$Reason}{$ToUser}) if ($Detail >= 5);
           $TotalError[$ErrorIndex] += $StatRejected{$Reason}{$ToUser};
       }
    }
@@ -2033,9 +2132,9 @@
    eval "$PrintCond" if ($Detail >= 3);
    print "\n\nMail Deferred:" if ($Detail >= 3);
    foreach $Reason (sort keys %StatDeferred) {
-      print "\n    $Reason:" if ($Detail >= 5);
+      print "\n    $Reason" if ($Detail >= 5);
       foreach $ToUser (keys %{$StatDeferred{$Reason}}) {
-          print "\n        To: $ToUser: $StatDeferred{$Reason}{$ToUser} Time(s)" if ($Detail >= 5);
+          PrettyTimes("        To: $ToUser", $StatDeferred{$Reason}{$ToUser}) if ($Detail >= 5);
           $TotalError[$ErrorIndex] += $StatDeferred{$Reason}{$ToUser};
       }
    }
@@ -2049,7 +2148,7 @@
    print "\n\nForwarding errors:" if ($Detail >= 3);
    my $FECount = CountOrder(%ForwardErrors);
    foreach $ThisOne (sort $FECount keys %ForwardErrors) {
-      print "\n    $ThisOne: $ForwardErrors{$ThisOne} Time(s)" if ($Detail >= 5);
+      PrettyTimes("    $ThisOne", $ForwardErrors{$ThisOne}) if ($Detail >= 5);
           $TotalError[$ErrorIndex] += $ForwardErrors{$ThisOne};
    }
    print "\n\tTotal:  $TotalError[$ErrorIndex]" if ($Detail >= 3);
@@ -2062,7 +2161,7 @@
    eval "$PrintCond" if ($Detail >= 3);
    print "\n\nBlackHole Totals:" if ($Detail >= 3);
    foreach $ThisOne (sort keys %BlackHoles) {
-      print "\n    $ThisOne: $BlackHoles{$ThisOne} Time(s)" if ($Detail >= 5);
+      PrettyTimes("    $ThisOne", $BlackHoles{$ThisOne}) if ($Detail >= 5);
       $TotalError[$ErrorIndex] += $BlackHoles{$ThisOne};
    }
    if ($Detail >= 10) {
@@ -2070,7 +2169,7 @@
       my $BlackHoleThreshold = $ENV{'sendmail_blackholethreshold'} || "1";
       foreach $ThisOne (sort keys %BlackHoled) {
          if ($BlackHoled{$ThisOne} >= $BlackHoleThreshold) {
-            print "\n    $ThisOne: $BlackHoled{$ThisOne} Times(s)";
+            PrettyTimes("    $ThisOne", $BlackHoled{$ThisOne});
          }
       }
    }
@@ -2096,7 +2195,7 @@
    eval "$PrintCond" if ($Detail >= 3);
    print "\n\nMail was rejected because of the following entries in the access database:" if ($Detail >= 3);
    foreach $ThisOne (sort keys %MailRejected) {
-      printf "\n    %s: %i Time(s)" , $ThisOne , $MailRejected{$ThisOne} if ($Detail >= 5);
+      PrettyTimes("    $ThisOne", $MailRejected{$ThisOne}) if ($Detail >= 5);
       $TotalError[$ErrorIndex] += $MailRejected{$ThisOne};
    }
    print "\n\tTotal:  $TotalError[$ErrorIndex]" if ($Detail >= 3);
@@ -2138,7 +2237,7 @@
 if (keys %RuleSets) {
    print "\n\nRuleset violations:";
    foreach $Reason (sort keys %RuleSets) {
-      print "\n    $Reason: $RuleSets{$Reason} Time(s)";
+      PrettyTimes("    $Reason", $RuleSets{$Reason});
       $TotalError[$ErrorIndex] += $RuleSets{$Reason};
    }
 }
@@ -2156,19 +2255,19 @@
       $TotalCount += $ErrorCount;
    }
 }
-if ($TotalCount > 0) {
-   eval "$PrintCond" if ($Detail >= 3);
-   print "\n\nTotal SMTP Session, Message, and Recipient Errors handled by Sendmail:  $TotalCount"
-      if ($Detail >= 3);
+if ( ($TotalCount > 0) && ($Detail >= 3)) {
+   eval "$PrintCond";
+   print "\n\nSummary of SMTP Session, Message, and Recipient Errors handled by Sendmail:";
+   print "\n\tTotal:  $TotalCount";
 }
 
 if (keys %MilterErrors) {
    eval "$PrintCond";
    print "\n\nMilter Errors:\n";
-   foreach $Milter (sort {$a cmp $b} keys %MilterErrors) {
+   foreach my $Milter (sort {$a cmp $b} keys %MilterErrors) {
       print "   $Milter:\n";
       foreach $Error (sort {$a cmp $b} keys %{$MilterErrors{$Milter}}) {
-         print "      $Error: $MilterErrors{$Milter}{$Error} Time(s)\n";
+         PrettyTimes("      $Error", $MilterErrors{$Milter}{$Error});
       }
    }
 }
@@ -2177,7 +2276,7 @@
    $HeaderPrinted = 1;
    print "\n\n**Unmatched Entries**";
    foreach my $line (sort {$OtherList{$b}<=>$OtherList{$a} } keys %OtherList) {
-      print "\n   $line: $OtherList{$line} Time(s)";
+      PrettyTimes("   $line", $OtherList{$line});
    }
 }
 

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.


------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140