]> git.sven.stormbind.net Git - sven/pflogsumm.git/commitdiff
New upstream version 1.1.8 upstream/1.1.8
authorSven Hoexter <sven@stormbind.net>
Tue, 27 May 2025 15:22:27 +0000 (17:22 +0200)
committerSven Hoexter <sven@stormbind.net>
Tue, 27 May 2025 15:22:27 +0000 (17:22 +0200)
ChangeLog
pffrombyto.1
pflogsumm
pflogsumm.1
pftobyfrom.1

index 656ced206251232ea275f766c471e63d2e96154c..9d1c5941d5c494a1f6180dc5b4c539100fdbd820 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,104 @@ ChangeLog for pflogsumm
      http://jimsun.LinxNet.com/postfix_contrib.html.]
 
 
+rel-1.1.8      20250525
+
+    Removed the following deprecated options
+
+       --no_bounce_detail
+       --no_deferral_detail
+       --no_reject_detail
+       --no_smtpd_warnings
+
+    pflogsumm no longer accepts underscores ("_") as part of option
+    names. (Note: this finally addresses four-year-old Bugzilla bug
+    1931403.)
+
+    Code cleanup: Changed some variable constants to true constants.
+
+    Added test for reject type "BDAT" (Thanks to Maxim,
+    admin-at-modum-dot-by, for the heads up.)
+
+    Fixed unitialized $hostID value bug-reported to OpenSUSE. Thanks to
+    Sven Uebelacker (uebelhacker) for the bug report.
+
+       I *suspect* that, with an earlier fix I applied, that undefined
+       value will no longer occur, but this fix certainly won't hurt
+       anything—just in case.
+
+    Added -srs-mung option. Thanks and a tip o' the hat to Tom Hendrikx
+    (tom-at-whyscreem-dot-net) for the contribution.
+    
+rel-1.1.7      20250524
+
+    Added parsing for "Recipient address triggers...," as in DISCARD on
+    recipient actions.
+
+    Improved "deferred" and "bounce" detail
+
+    Added --use-orig-to switch, which causes the value of the "orig_to"
+    field to be reported, rather than that of the "to" field, when
+    "orig_to" is present.  (This was long-ago requested by Tony Earnshaw
+    and was overlooked by me.  My apologies, Tony.)
+
+    Added --rej-add-to switch, which adds the target email address to
+    sender and client reject reports.  (This was also long-ago requested
+    by Tony Earnshaw.)
+
+       For client rejects this could have created an ambiguity: If only
+       either --rej-add-from or --rej-add-to was specified, you'd have
+       no way of knowing, after-the-fact, which address was reported.
+       So "<" and ">" have been added, to indicate "from" and "to,"
+       respectively.  If both are specified, you get "from -> to."
+
+    Added --colwidth switch, to allow adjustment of report's output width.
+    (Default remains at 80 columns.)
+
+       N.B.: --verbose-msg-detail overrides
+
+    Now condenses deferrals in a manner similar to bounces.
+
+    Now condenses warnings in a manner similar to reject reports.
+
+    Made count reporting more consistent.  Now each reporting section
+    shows the item count in parenthesis (e.g.: "(nn)") or, if the
+    count is limited and less than full count, as "(top nn of nn)".
+    Suggestion by Michael Rasmussen (michael-at-michaelsnet-dot-us),
+    tho not done in the way he suggested.
+
+    Fixed policyd-spf-perl log line detection.
+
+    Fixed conversation/lost connection while sending/receiving deferral
+    detection
+
+    Improved "said:" string trimmer to be a mite less aggressive when it
+    didn't have to be.
+
+    Improved reject report consolidation.
+
+    In Host/Domain Summary: Message Delivery: Changed "sent cnt" to "msg
+    cnt" since we're talking about messages delivered, not necessarily
+    "sent," per se.
+
+    Fixed bug in smtpd "disconnect from" parsing and streamlined.
+
+    Removed superfluous additional "bounced" processing
+
+    Caught another warning message
+
+    Catch "Server configuration error" lines.
+
+    Enhanced FQDN/IP addr/domain name parsing (in gimme_domain()):
+
+       . More exhaustive IPv6 address check
+       . Handles IPv6-mapped-IPv4 addresses
+       . Properly handles the newer, longer TLDs
+       . Treats in-addr.arpa and ip6.arpa FQNS like "unknown"s
+       . More streamlined
+
+    Added pffrombyto and pftobyfrom command-line utilities to the
+    distribution. (Actually in 1.1.6.)
+
 rel-1.1.6      20250522
 
     Renamed from "pflogsumm.pl" to "pflogsumm"
index 3a0e77c2f9f62690776592ad3e2c6b8120eaf325..fbe0bad859f5b158b938060306c059334a40a8d2 100644 (file)
@@ -55,7 +55,7 @@
 .\" ========================================================================
 .\"
 .IX Title "PFFROMBYTO 1"
-.TH PFFROMBYTO 1 2025-05-22 1.1.6 "User Contributed Perl Documentation"
+.TH PFFROMBYTO 1 2025-05-22 1.1.8 "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
index cb3b8a924d5106ed96b04b5d406159bbe305dc4d..42a48e34f29e717ab7177267b08b19ece370094e 100755 (executable)
--- a/pflogsumm
+++ b/pflogsumm
@@ -6,18 +6,18 @@ eval 'exec perl -S $0 "$@"'
 
 pflogsumm - Produce Postfix MTA logfile summary
 
-Copyright (C) 1998-2025 by James S. Seymour, Release 1.1.6
+Copyright (C) 1998-2025 by James S. Seymour, Release 1.1.8
 
 =head1 SYNOPSIS
 
     pflogsumm -[eq] [-d <today|yesterday>] [--detail <cnt>]
-       [--bounce-detail <cnt>] [--deferral-detail <cnt>]
+       [--bounce-detail <cnt>] [--colwidth <n>] [--deferral-detail <cnt>]
        [-h <cnt>] [-i|--ignore-case] [--iso-date-time] [--mailq]
-       [-m|--uucp-mung] [--no-no-msg-size] [--problems-first]
-       [--rej-add-from] [--reject-detail <cnt>] [--smtp-detail <cnt>]
-       [--smtpd-stats] [--smtpd-warning-detail <cnt>]
-       [--syslog-name=string] [-u <cnt>] [--verbose-msg-detail]
-       [--verp-mung[=<n>]] [--zero-fill] [file1 [filen]]
+       [-m|--uucp-mung] [--no-no-msg-size] [--problems-first] [--rej-add-from]
+       [--rej-add-to] [--reject-detail <cnt>] [--smtp-detail <cnt>]
+       [--smtpd-stats] [--smtpd-warning-detail <cnt>] [--srs-mung]
+       [--syslog-name=string] [-u <cnt>] [--use-orig-to]
+       [--verbose-msg-detail] [--verp-mung[=<n>] [--zero-fill] [file1 [filen]]
 
     pflogsumm -[help|version]
 
@@ -41,6 +41,13 @@ Copyright (C) 1998-2025 by James S. Seymour, Release 1.1.6
                   Limit detailed bounce reports to the top <cnt>.  0
                   to suppress entirely.
 
+    --colwidth <n>
+
+                   Maximum report output width.  Default is 80 columns.
+                   0 = unlimited.
+
+                   N.B.: --verbose-msg-detail overrides
+
     -d today       generate report for just today
     -d yesterday   generate report for just "yesterday"
 
@@ -114,24 +121,6 @@ Copyright (C) 1998-2025 by James S. Seymour, Release 1.1.6
                   is in $PATH.  See "$mailqCmd" variable to path thisi
                   if desired.)
 
-    --no_bounce_detail
-    --no_deferral_detail
-    --no_reject_detail
-
-                  These switches are deprecated in favour of
-                  --bounce-detail, --deferral-detail and
-                  --reject-detail, respectively.
-
-                   Suppresses the printing of the following detailed
-                   reports, respectively:
-
-                       message bounce detail (by relay)
-                       message deferral detail
-                       message reject detail
-
-                   See also: "-u" and "-h" for further report-limiting
-                             options.
-
     --no-no-msg-size
 
                    Do not emit report on "Messages with no size data".
@@ -145,15 +134,6 @@ Copyright (C) 1998-2025 by James S. Seymour, Release 1.1.6
                    normally reported by pflogsumm as "Messages with no
                    size data."
 
-    --no-smtpd-warnings
-
-                  This switch is deprecated in favour of
-                  smtpd-warning-detail
-
-                   On a busy mail server, say at an ISP, SMTPD warnings
-                   can result in a rather sizeable report.  This option
-                   turns reporting them off.
-
     --problems-first
 
                    Emit "problems" reports (bounces, defers, warnings,
@@ -170,6 +150,11 @@ Copyright (C) 1998-2025 by James S. Seymour, Release 1.1.6
                   note: headings for warning, fatal, and "master"
                   messages will always be printed.
 
+    --rej-add-to
+
+                   For sender reject reports: Add the intended recipient
+                  address.
+
     --reject-detail <cnt>
 
                   Limit detailed smtpd reject, warn, hold and discard
@@ -193,6 +178,23 @@ Copyright (C) 1998-2025 by James S. Seymour, Release 1.1.6
                   Limit detailed smtpd warnings reports to the top <cnt>.
                   0 to suppress entirely.
 
+    --srs-mung
+
+                   Undo SRS address munging.
+
+                  If your postfix install has an SRS plugin running, many
+                   addresses in the report will contain the SRS-formatted
+                   email addresses, also for non-local adresses (f.i.
+                   senders). This option will try to undo the "damage".
+
+                   Addresses of the form:
+
+                     SRS0=A6cv=PT=sender.example.com=support@srs.example.net
+
+                   will be reformatted to their original value:
+
+                     support@sender.example.com
+
     --syslog-name=name
 
                   Set syslog-name to look for for Postfix log entries.
@@ -211,6 +213,11 @@ Copyright (C) 1998-2025 by James S. Seymour, Release 1.1.6
                    See also: "-h" and "--*-detail" options for further
                             report-limiting options.
 
+    --use-orig-to
+
+                 Where "orig_to" fields are found, report that in place
+                 of the "to" address.
+
     --verbose-msg-detail
 
                    For the message deferral, bounce and reject summaries:
@@ -284,6 +291,8 @@ Copyright (C) 1998-2025 by James S. Seymour, Release 1.1.6
 
 =head1 SEE ALSO
 
+    pffrombyto, pftobyfrom
+
     The pflogsumm FAQ: pflogsumm-faq.txt.
 
 =head1 NOTES
@@ -402,26 +411,26 @@ eval { require Date::Calc };
 my $hasDateCalc = $@ ? 0 : 1;
 
 my $mailqCmd = "mailq";
-my $release = "1.1.6";
+my $release = "1.1.8";
 
 # Variables and constants used throughout pflogsumm
 use vars qw(
     $progName
     $usageMsg
     %opts
-    $divByOneKAt $divByOneMegAt $oneK $oneMeg
     @monthNames %monthNums $thisYr $thisMon
-    $msgCntI $msgSizeI $msgDfrsI $msgDlyAvgI $msgDlyMaxI
     $isoDateTime
 );
 
 # Some constants used by display routines.  I arbitrarily chose to
 # display in kilobytes and megabytes at the 512k and 512m boundaries,
 # respectively.  Season to taste.
-$divByOneKAt   = 524288;       # 512k
-$divByOneMegAt = 536870912;    # 512m
-$oneK          = 1024;         # 1k
-$oneMeg        = 1048576;      # 1m
+use constant {
+    DIV_BY_ONE_K_AT   => 524288,       # 512k
+    DIV_BY_ONE_MEG_AT => 536870912,    # 512m
+    ONE_K             => 1024,         # 1k
+    ONE_MEG           => 1048576,      # 1m
+};
 
 # Constants used throughout pflogsumm
 @monthNames = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
@@ -440,15 +449,18 @@ my (%sendgUser, $sendgUserCnt);
 # Per-domain data
 my (%recipDom, $recipDomCnt);  # recipient domain data
 my (%sendgDom, $sendgDomCnt);  # sending domain data
+
 # Indexes for arrays in above
-$msgCntI    = 0;       # message count
-$msgSizeI   = 1;       # total messages size
-$msgDfrsI   = 2;       # number of defers
-$msgDlyAvgI = 3;       # total of delays (used for averaging)
-$msgDlyMaxI = 4;       # max delay
+use constant {
+    MSG_CNT_I     => 0,    # message count
+    MSG_SIZE_I    => 1,    # total messages size
+    MSG_DFRS_I    => 2,    # number of defers
+    MSG_DLY_AVG_I => 3,    # total of delays (used for averaging)
+    MSG_DLY_MAX_I => 4,    # max delay
+};
 
 my (
-    $cmd, $qid, $addr, $size, $relay, $status, $delay,
+    $cmd, $qid, $addr, $orig_to, $size, $relay, $status, $delay,
     $dateStr, $dateStrRFC3339,
     %panics, %fatals, %warnings, %masterMsgs,
     %msgSizes,
@@ -490,22 +502,16 @@ for (0 .. 23) {
 
 $usageMsg =
     "usage: $progName -[eq] [-d <today|yesterday>] [--detail <cnt>]
-       [--bounce-detail <cnt>] [--deferral-detail <cnt>]
+       [--bounce-detail <cnt>] [--colwidth <n>] [--deferral-detail <cnt>]
        [-h <cnt>] [-i|--ignore-case] [--iso-date-time] [--mailq]
-       [-m|--uucp-mung] [--no-no-msg-size] [--problems-first]
-       [--rej-add-from] [--reject-detail <cnt>] [--smtp-detail <cnt>]
-       [--smtpd-stats] [--smtpd-warning-detail <cnt>]
-       [--syslog-name=string] [-u <cnt>] [--verbose-msg-detail]
+       [-m|--uucp-mung] [--no-no-msg-size] [--problems-first] [--rej-add-from]
+       [--rej-add-to] [--reject-detail <cnt>] [--smtp-detail <cnt>]
+       [--smtpd-stats] [--smtpd-warning-detail <cnt>] [--srs-mung]
+       [--syslog-name=string] [-u <cnt>] [--use-orig-to] [--verbose-msg-detail]
        [--verp-mung[=<n>]] [--zero-fill] [file1 [filen]]
 
        $progName --[version|help]";
 
-# Accept either "_"s or "-"s in --switches
-foreach (@ARGV) {
-    last if($_ eq "--");
-    tr/_/-/ if(/^--\w/);
-}
-
 # Some pre-inits for convenience
 $isoDateTime = 0;      # Don't use ISO date/time formats
 GetOptions(
@@ -521,20 +527,20 @@ GetOptions(
     "iso-date-time"            => \$isoDateTime,
     "mailq"                    => \$opts{'mailq'},
     "m"                        => \$opts{'m'},
-    "no-bounce-detail"         => \$opts{'noBounceDetail'},
-    "no-deferral-detail"       => \$opts{'noDeferralDetail'},
     "no-no-msg-size"           => \$opts{'noNoMsgSize'},
-    "no-reject-detail"         => \$opts{'noRejectDetail'},
-    "no-smtpd-warnings"        => \$opts{'noSMTPDWarnings'},
     "problems-first"           => \$opts{'pf'},
     "q"                        => \$opts{'q'},
     "rej-add-from"             => \$opts{'rejAddFrom'},
+    "rej-add-to"               => \$opts{'rejAddTo'},
     "reject-detail=i"          => \$opts{'rejectDetail'},
+    "colwidth=i"               => \$opts{'colWidth'},
     "smtp-detail=i"            => \$opts{'smtpDetail'},
     "smtpd-stats"              => \$opts{'smtpdStats'},
     "smtpd-warning-detail=i"   => \$opts{'smtpdWarnDetail'},
+    "srs-mung"                 => \$opts{'srsMung'},
     "syslog-name=s"            => \$opts{'syslogName'},
     "u=i"                      => \$opts{'u'},
+    "use-orig-to"              => \$opts{'useOrigTo'},
     "uucp-mung"                => \$opts{'m'},
     "verbose-msg-detail"       => \$opts{'verbMsgDetail'},
     "verp-mung:i"              => \$opts{'verpMung'},
@@ -550,24 +556,8 @@ $opts{'deferralDetail'} = -1 unless(defined($opts{'deferralDetail'}));
 $opts{'smtpDetail'} = -1 unless(defined($opts{'smtpDetail'}));
 $opts{'smtpdWarnDetail'} = -1 unless(defined($opts{'smtpdWarnDetail'}));
 $opts{'rejectDetail'} = -1 unless(defined($opts{'rejectDetail'}));
-
-# These go away eventually
-if(defined($opts{'noBounceDetail'})) {
-    $opts{'bounceDetail'} = 0;
-    warn "$progName: \"no_bounce_detail\" is deprecated, use \"bounce-detail=0\" instead\n"
-}
-if(defined($opts{'noDeferralDetail'})) {
-    $opts{'deferralDetail'} = 0;
-    warn "$progName: \"no_deferral_detail\" is deprecated, use \"deferral-detail=0\" instead\n"
-}
-if(defined($opts{'noRejectDetail'})) {
-    $opts{'rejectDetail'} = 0;
-    warn "$progName: \"no_reject_detail\" is deprecated, use \"reject-detail=0\" instead\n"
-}
-if(defined($opts{'noSMTPDWarnings'})) {
-    $opts{'smtpdWarnDetail'} = 0;
-    warn "$progName: \"no_smtpd_warnings\" is deprecated, use \"smtpd-warning-detail=0\" instead\n"
-}
+$opts{'colWidth'} = 0 if($opts{'verbMsgDetail'});
+$opts{'colWidth'} = -1 unless(defined($opts{'colWidth'}));
 
 # If --detail was specified, set anything that's not enumerated to it
 if(defined($opts{'detail'})) {
@@ -671,7 +661,8 @@ while(<>) {
        /\/cleanup\[\d+\]: .*?\b((?:milter-)?reject|warning|hold|discard): (header|body|END-OF-MESSAGE) (.*)$/) == 3)
     {
        $rejRmdr =~ s/( from \S+?)?; from=<.*$// unless($opts{'verbMsgDetail'});
-       $rejRmdr = string_trimmer($rejRmdr, 64, $opts{'verbMsgDetail'});
+       # FIXME: In retrospect: I've no idea where I came up with the magic numbers I pass to this function.
+       $rejRmdr = string_trimmer($rejRmdr, 64);
        if($rejSubTyp eq "reject" or $rejSubTyp eq "milter-reject") {
            ++$rejects{$cmd}{$rejReas}{$rejRmdr} unless($opts{'rejectDetail'} == 0);
            ++$msgsRjctd;
@@ -689,17 +680,38 @@ while(<>) {
        ++${$msgsPerDay{$revMsgDateStr}}[4];
     } elsif($qid eq 'warning') {
        (my $warnReas = $logRmdr) =~ s/^.*warning: //;
-       $warnReas = string_trimmer($warnReas, 66, $opts{'verbMsgDetail'});
-       unless($cmd eq "smtpd" && $opts{'noSMTPDWarnings'}) {
+       unless($opts{'verbMsgDetail'}) {
+           # Condense smtpd and other warnings
+           $warnReas =~ s/^(Unable to look up (?:MX|NS) host) for .+(: Host not found(?:,try again)?)/$1$2/ ||
+           $warnReas =~ s/^(hostname ).+ (does not resolve to address) [0-9A-F:\.]+$/$1$2/ ||
+           $warnReas =~ s/^(hostname ).+ (does not resolve to address) .+(: hostname nor servname provided, or not known)$/$1$2$3/ ||
+           $warnReas =~ s/^(Unable to look up (?:MX|NS) host ).+ (for (?:Sender address|Client host|Helo command)) .+(: (?:hostname nor servname provided, or not known|No address associated with hostname))$/$1$2$3/ ||
+           $warnReas =~ s/^(malformed domain name in resource data of MX record) for .*$/$1/ ||
+           $warnReas =~ s/^(numeric domain name in resource data of (?:MX|NS) record) for .*$/$1/ ||
+           $warnReas =~ s/^(numeric hostname): .*$/$1/ ||
+           $warnReas =~ s/^(valid_hostname: invalid character) .*$/$1/ ||
+           $warnReas =~ s/^[0-9A-F:\.]+ (address not listed for hostname) .*$/$1/ ||
+           $warnReas =~ s/^[0-9A-F]+: (queue file size limit exceeded)$/$1/ ||
+           $warnReas =~ s/^[^:]+: (SASL (?:LOGIN|PLAIN|CRAM-MD5) authentication failed(?:: Invalid authentication mechanism)?).*$/$1/ ||
+           $warnReas =~ s/^(Illegal address syntax )from .+ (in (?:MAIL|RCPT) command): .*$/$1$2/ ||
+           $warnReas =~ s/^(non-SMTP command) from .+?(: \S+) .*$/$1$2/ ||
+           $warnReas =~ s/^(Connection concurrency limit exceeded: \d+ )from \S+ (for service .+)$/$1$2/ ||
+           $warnReas =~ s/^[0-9A-F:\.]+ (hostname ).+ (verification failed: No address associated with hostname)$/$1$2/ ||
+           $warnReas =~ s/^[\w\.-]+: (RBL lookup error: Host or domain name not found. Name service error )for name=[\w\.-]+ (type=.+: Host not found, try again)$/$1$2/ ||
+           $warnReas =~ s/^.+((?:postfix-)?policyd-spf-perl: process )id \d+: (command time limit exceeded)$/$1$2/ ||
+           $warnReas =~ s/(process .+) pid \d+ (exit status \d+)/$1 $2/;
+       }
+       $warnReas = string_trimmer($warnReas, 66);
+       unless($cmd eq "smtpd" && $opts{'smtpdWarnDetail'} == 0) {
            ++$warnings{$cmd}{$warnReas};
        }
     } elsif($qid eq 'fatal') {
        (my $fatalReas = $logRmdr) =~ s/^.*fatal: //;
-       $fatalReas = string_trimmer($fatalReas, 66, $opts{'verbMsgDetail'});
+       $fatalReas = string_trimmer($fatalReas, 66);
        ++$fatals{$cmd}{$fatalReas};
     } elsif($qid eq 'panic') {
        (my $panicReas = $logRmdr) =~ s/^.*panic: //;
-       $panicReas = string_trimmer($panicReas, 66, $opts{'verbMsgDetail'});
+       $panicReas = string_trimmer($panicReas, 66);
        ++$panics{$cmd}{$panicReas};
     } elsif($qid eq 'reject') {
        proc_smtpd_reject($logRmdr, \%rejects, \$msgsRjctd, \$rejPerHr[$msgHr],
@@ -752,9 +764,8 @@ while(<>) {
                @{$connTime{$1}} =
                    ($msgYr, $msgMon + 1, $msgDay, $msgHr, $msgMin, $msgSec);
            } elsif($logRmdr =~ /: disconnect from /) {
-               my ($pid, $hostID) = $logRmdr =~ /\/smtpd\[(\d+)\]: disconnect from (.+)$/;
-               if(exists($connTime{$pid})) {
-                   $hostID = gimme_domain($hostID);
+               my ($pid, $hostID) = $logRmdr =~ /\/smtpd\[(\d+)\]: disconnect from (.+?)( unknown=\d+\/\d+)?( commands=\d+\/\d+)?$/;
+               if(exists($connTime{$pid}) && ($hostID = gimme_domain($hostID))) {
                    my($d, $h, $m, $s) = Delta_DHMS(@{$connTime{$pid}},
                        $msgYr, $msgMon + 1, $msgDay, $msgHr, $msgMin, $msgSec);
                    delete($connTime{$pid});    # dispose of no-longer-needed item
@@ -768,17 +779,20 @@ while(<>) {
                        ${$smtpdPerDay{$revMsgDateStr}}[1] = 0;
                        ${$smtpdPerDay{$revMsgDateStr}}[2] = 0;
                    }
+
                    ${$smtpdPerDay{$revMsgDateStr}}[1] += $tSecs;
                    ${$smtpdPerDay{$revMsgDateStr}}[2] = $tSecs
                        if($tSecs > ${$smtpdPerDay{$revMsgDateStr}}[2]);
 
-                   unless(${$smtpdPerDom{$hostID}}[0]++) {
-                       ${$smtpdPerDom{$hostID}}[1] = 0;
-                       ${$smtpdPerDom{$hostID}}[2] = 0;
+                   if($hostID){
+                       unless(${$smtpdPerDom{$hostID}}[0]++) {
+                           ${$smtpdPerDom{$hostID}}[1] = 0;
+                           ${$smtpdPerDom{$hostID}}[2] = 0;
+                       }
+                       ${$smtpdPerDom{$hostID}}[1] += $tSecs;
+                       ${$smtpdPerDom{$hostID}}[2] = $tSecs
+                           if($tSecs > ${$smtpdPerDom{$hostID}}[2]);
                    }
-                   ${$smtpdPerDom{$hostID}}[1] += $tSecs;
-                   ${$smtpdPerDom{$hostID}}[2] = $tSecs
-                       if($tSecs > ${$smtpdPerDom{$hostID}}[2]);
 
                    ++$smtpdConnCnt;
                    $smtpdTotTime += $tSecs;
@@ -797,6 +811,7 @@ while(<>) {
                $addr =~ s/(@.+)/\L$1/ unless($opts{'i'});
                $addr = lc($addr) if($opts{'i'});
                $addr = verp_mung($addr);
+               $addr = srs_mung($addr);
            } else {
                $addr = "from=<>"
            }
@@ -811,19 +826,20 @@ while(<>) {
                    $domAddr = $rcvdMsg{$qid} eq "pickup"? $addr : $rcvdMsg{$qid};
                }
                ++$sendgDomCnt
-                   unless(${$sendgDom{$domAddr}}[$msgCntI]);
-               ++${$sendgDom{$domAddr}}[$msgCntI];
-               ${$sendgDom{$domAddr}}[$msgSizeI] += $size;
-               ++$sendgUserCnt unless(${$sendgUser{$addr}}[$msgCntI]);
-               ++${$sendgUser{$addr}}[$msgCntI];
-               ${$sendgUser{$addr}}[$msgSizeI] += $size;
+                   unless(${$sendgDom{$domAddr}}[MSG_CNT_I]);
+               ++${$sendgDom{$domAddr}}[MSG_CNT_I];
+               ${$sendgDom{$domAddr}}[MSG_SIZE_I] += $size;
+               ++$sendgUserCnt unless(${$sendgUser{$addr}}[MSG_CNT_I]);
+               ++${$sendgUser{$addr}}[MSG_CNT_I];
+               ${$sendgUser{$addr}}[MSG_SIZE_I] += $size;
                $sizeRcvd += $size;
                delete($rcvdMsg{$qid});         # limit hash size
            }
        }
-       elsif((($addr, $relay, $delay, $status, $toRmdr) = $logRmdr =~
-               /to=<([^>]*)>, (?:orig_to=<[^>]*>, )?relay=([^,]+), (?:conn_use=[^,]+, )?delay=([^,]+), (?:delays=[^,]+, )?(?:dsn=[^,]+, )?status=(\S+)(.*)$/) >= 4)
+       elsif((($addr, $orig_to, $relay, $delay, $status, $toRmdr) = $logRmdr =~
+               /to=<([^>]*)>, (?:orig_to=<([^>]*)>, )?relay=([^,]+), (?:conn_use=[^,]+, )?delay=([^,]+), (?:delays=[^,]+, )?(?:dsn=[^,]+, )?status=(\S+)(.*)$/) >= 4)
        {
+           $addr = $orig_to if($opts{'useOrigTo'} && $orig_to);
 
            if($opts{'m'} && $addr =~ /^(.*!)*([^!]+)!([^!@]+)@([^\.]+)$/) {
                $addr = "$4!" . ($1? "$1" : "") . $3 . "\@$2";
@@ -839,28 +855,28 @@ while(<>) {
                    ++$msgsFwdd;
                    next;
                }
-               ++$recipDomCnt unless(${$recipDom{$domAddr}}[$msgCntI]);
-               ++${$recipDom{$domAddr}}[$msgCntI];
-               ${$recipDom{$domAddr}}[$msgDlyAvgI] += $delay;
-               if(! ${$recipDom{$domAddr}}[$msgDlyMaxI] ||
-                  $delay > ${$recipDom{$domAddr}}[$msgDlyMaxI])
+               ++$recipDomCnt unless(${$recipDom{$domAddr}}[MSG_CNT_I]);
+               ++${$recipDom{$domAddr}}[MSG_CNT_I];
+               ${$recipDom{$domAddr}}[MSG_DLY_AVG_I] += $delay;
+               if(! ${$recipDom{$domAddr}}[MSG_DLY_MAX_I] ||
+                  $delay > ${$recipDom{$domAddr}}[MSG_DLY_MAX_I])
                {
-                   ${$recipDom{$domAddr}}[$msgDlyMaxI] = $delay
+                   ${$recipDom{$domAddr}}[MSG_DLY_MAX_I] = $delay
                }
-               ++$recipUserCnt unless(${$recipUser{$addr}}[$msgCntI]);
-               ++${$recipUser{$addr}}[$msgCntI];
+               ++$recipUserCnt unless(${$recipUser{$addr}}[MSG_CNT_I]);
+               ++${$recipUser{$addr}}[MSG_CNT_I];
                ++$dlvPerHr[$msgHr];
                ++${$msgsPerDay{$revMsgDateStr}}[1];
                ++$msgsDlvrd;
                # DEBUG DEBUG DEBUG
                #print STDERR "Delivered: $qid\n";
                if($msgSizes{$qid}) {
-                   ${$recipDom{$domAddr}}[$msgSizeI] += $msgSizes{$qid};
-                   ${$recipUser{$addr}}[$msgSizeI] += $msgSizes{$qid};
+                   ${$recipDom{$domAddr}}[MSG_SIZE_I] += $msgSizes{$qid};
+                   ${$recipUser{$addr}}[MSG_SIZE_I] += $msgSizes{$qid};
                    $sizeDlvrd += $msgSizes{$qid};
                } else {
-                   ${$recipDom{$domAddr}}[$msgSizeI] += 0;
-                   ${$recipUser{$addr}}[$msgSizeI] += 0;
+                   ${$recipDom{$domAddr}}[MSG_SIZE_I] += 0;
+                   ${$recipUser{$addr}}[MSG_SIZE_I] += 0;
                    $noMsgSize{$qid} = $addr unless($opts{'noNoMsgSize'});
                    push(@{$msgDetail{$qid}}, "(sender not in log)") if($opts{'e'});
                    # put this back later? mebbe with -v?
@@ -870,22 +886,37 @@ while(<>) {
            } elsif($status eq 'deferred') {
                unless($opts{'deferralDetail'} == 0) {
                    my ($deferredReas) = $logRmdr =~ /, status=deferred \(([^\)]+)/;
-                   unless(defined($opts{'verbMsgDetail'})) {
-                       $deferredReas = said_string_trimmer($deferredReas, 65);
-                       $deferredReas =~ s/^\d{3} //;
-                       $deferredReas =~ s/^connect to //;
+                   if(!defined($opts{'verbMsgDetail'})) {
+                       my ($host, $reason, $moreReason);       # More ugliness :/
+                       unless((($host, $reason) = ($deferredReas =~ /^host (\S+) (?:said|refused to talk to me): ([^(]+)/)) ||
+                              (($host, $reason) = ($deferredReas =~ /^(?:delivery temporarily suspended: )?connect to (.+?(?::\d+)?): ([^)]+)$/)) ||
+                              (($host, $reason) = ($deferredReas =~ /^cannot (?:append to file|update mailbox) ([^:.]+)[:.] (.+)$/)) ||
+                              (($reason, $host, $moreReason) = ($deferredReas =~ /^.*(Name service error )for (?:domain |name=)?([^: ]+):? (.+)$/)) ||
+                              (($reason, $host, $moreReason) = ($deferredReas =~ /^((?:conversation|lost connection) )with (\S+) ((?:timed out )?while (receiving|sending) .+)$/)) ||
+                              (($reason, $host, $moreReason) = ($deferredReas =~ /^(delivery temporarily suspended: )connect to ([^:]+): (.+)$/))
+                             )
+                       {
+                           $host = "unrecognized deferral reason(s)";
+                           $reason = $deferredReas;
+                       }
+
+                       $reason .= $moreReason if($moreReason); # ick
+                       # Finally...
+                       $reason = said_string_trimmer($reason, 66);
+                       ++$deferred{$cmd}{$host}{$reason};
+                   } else {
+                       ++$deferred{$cmd}{$deferredReas};
                    }
-                   ++$deferred{$cmd}{$deferredReas};
                }
                 ++$dfrPerHr[$msgHr];
                ++${$msgsPerDay{$revMsgDateStr}}[2];
                ++$msgsDfrdCnt;
                ++$msgsDfrd unless($msgDfrdFlgs{$qid}++);
-               ++${$recipDom{$domAddr}}[$msgDfrsI];
-               if(! ${$recipDom{$domAddr}}[$msgDlyMaxI] ||
-                  $delay > ${$recipDom{$domAddr}}[$msgDlyMaxI])
+               ++${$recipDom{$domAddr}}[MSG_DFRS_I];
+               if(! ${$recipDom{$domAddr}}[MSG_DLY_MAX_I] ||
+                  $delay > ${$recipDom{$domAddr}}[MSG_DLY_MAX_I])
                {
-                   ${$recipDom{$domAddr}}[$msgDlyMaxI] = $delay
+                   ${$recipDom{$domAddr}}[MSG_DLY_MAX_I] = $delay
                }
            } elsif($status eq 'bounced') {
                unless($opts{'bounceDetail'} == 0) {
@@ -998,10 +1029,10 @@ if(defined($opts{'smtpdStats'})) {
     print_domain_smtpd_summary(\%smtpdPerDom, $opts{'h'});
 }
 
-print_user_data(\%sendgUser, "Senders by message count", $msgCntI, $opts{'u'}, $opts{'q'});
-print_user_data(\%recipUser, "Recipients by message count", $msgCntI, $opts{'u'}, $opts{'q'});
-print_user_data(\%sendgUser, "Senders by message size", $msgSizeI, $opts{'u'}, $opts{'q'});
-print_user_data(\%recipUser, "Recipients by message size", $msgSizeI, $opts{'u'}, $opts{'q'});
+print_user_data(\%sendgUser, "Senders by message count", MSG_CNT_I, $opts{'u'}, $opts{'q'});
+print_user_data(\%recipUser, "Recipients by message count", MSG_CNT_I, $opts{'u'}, $opts{'q'});
+print_user_data(\%sendgUser, "Senders by message size", MSG_SIZE_I, $opts{'u'}, $opts{'q'});
+print_user_data(\%recipUser, "Recipients by message size", MSG_SIZE_I, $opts{'u'}, $opts{'q'});
 
 print_hash_by_key(\%noMsgSize, "Messages with no size data", 0, 1);
 
@@ -1109,30 +1140,32 @@ sub print_recip_domain_summary {
     local($hashRef) = $_[0];
     my($cnt) = $_[1];
     return if($cnt == 0);
-    my $topCnt = $cnt > 0? "(top $cnt)" : "";
+
+    my $rptCnt = keys %{$hashRef};
+    my $topCnt = "(" . ($cnt > 0 && $rptCnt > $cnt? "top $cnt of " : "") . "$rptCnt)";
     my $avgDly;
 
     print_subsect_title("Host/Domain Summary: Message Delivery $topCnt");
 
     print <<End_Of_Recip_Domain_Heading;
sent cnt  bytes   defers   avg dly max dly host/domain
msg cnt   bytes   defers   avg dly max dly host/domain
  -------- -------  -------  ------- ------- -----------
 End_Of_Recip_Domain_Heading
 
     foreach (reverse sort by_count_then_size keys(%$hashRef)) {
        # there are only delay values if anything was sent
-       if(${$hashRef->{$_}}[$msgCntI]) {
-           $avgDly = (${$hashRef->{$_}}[$msgDlyAvgI] /
-                      ${$hashRef->{$_}}[$msgCntI]);
+       if(${$hashRef->{$_}}[MSG_CNT_I]) {
+           $avgDly = (${$hashRef->{$_}}[MSG_DLY_AVG_I] /
+                      ${$hashRef->{$_}}[MSG_CNT_I]);
        } else {
            $avgDly = 0;
        }
        printf " %6d%s  %6d%s  %6d%s  %5.1f %s  %5.1f %s  %s\n",
-           adj_int_units(${$hashRef->{$_}}[$msgCntI]),
-           adj_int_units(${$hashRef->{$_}}[$msgSizeI]),
-           adj_int_units(${$hashRef->{$_}}[$msgDfrsI]),
+           adj_int_units(${$hashRef->{$_}}[MSG_CNT_I]),
+           adj_int_units(${$hashRef->{$_}}[MSG_SIZE_I]),
+           adj_int_units(${$hashRef->{$_}}[MSG_DFRS_I]),
            adj_time_units($avgDly),
-           adj_time_units(${$hashRef->{$_}}[$msgDlyMaxI]),
+           adj_time_units(${$hashRef->{$_}}[MSG_DLY_MAX_I]),
            $_;
        last if --$cnt == 0;
     }
@@ -1145,7 +1178,9 @@ sub print_sending_domain_summary {
     local($hashRef) = $_[0];
     my($cnt) = $_[1];
     return if($cnt == 0);
-    my $topCnt = $cnt > 0? "(top $cnt)" : "";
+
+    my $rptCnt = keys %{$hashRef};
+    my $topCnt = "(" . ($cnt > 0 && $rptCnt > $cnt? "top $cnt of " : "") . "$rptCnt)";
 
     print_subsect_title("Host/Domain Summary: Messages Received $topCnt");
 
@@ -1156,8 +1191,8 @@ End_Of_Sender_Domain_Heading
 
     foreach (reverse sort by_count_then_size keys(%$hashRef)) {
        printf " %6d%s  %6d%s  %s\n",
-           adj_int_units(${$hashRef->{$_}}[$msgCntI]),
-           adj_int_units(${$hashRef->{$_}}[$msgSizeI]),
+           adj_int_units(${$hashRef->{$_}}[MSG_CNT_I]),
+           adj_int_units(${$hashRef->{$_}}[MSG_SIZE_I]),
            $_;
        last if --$cnt == 0;
     }
@@ -1169,14 +1204,15 @@ sub print_user_data {
     my($hashRef, $title, $index, $cnt, $quiet) = @_;
     my $dottedLine;
     return if($cnt == 0);
-    $title = sprintf "%s%s", $cnt > 0? "top $cnt " : "", $title;
-    unless(%$hashRef) {
-       return if($quiet);
-       $dottedLine = ": none";
-    } else {
-       $dottedLine = "\n" . "-" x length($title);
-    }
-    printf "\n$title$dottedLine\n";
+
+    my $rptCnt = keys %{$hashRef};
+    $title .= " (" . ($cnt > 0 && $rptCnt > $cnt? "top $cnt of " : "") . "$rptCnt)";
+
+    return unless($rptCnt || ! $quiet);
+
+    $title .= "\n" . "-" x length($title) if($rptCnt);
+    printf "\n$title\n";
+
     foreach (map { $_->[0] }
             sort { $b->[1] <=> $a->[1] || $a->[2] cmp $b->[2] }
             map { [ $_, $hashRef->{$_}[$index], normalize_host($_) ] }
@@ -1277,7 +1313,9 @@ sub print_domain_smtpd_summary {
     local($hashRef) = $_[0];
     my($cnt) = $_[1];
     return if($cnt == 0);
-    my $topCnt = $cnt > 0? "(top $cnt)" : "";
+
+    my $rptCnt = keys %{$hashRef};
+    my $topCnt = "(" . ($cnt > 0 && $rptCnt > $cnt? "top $cnt of " : "") . "$rptCnt)";
     my $avgDly;
 
     print_subsect_title("Host/Domain Summary: SMTPD Connections $topCnt");
@@ -1365,10 +1403,12 @@ sub walk_nested_hash {
            my $hashVal2 = (each(%{$hashRef->{$_}}))[1];
            keys(%{$hashRef->{$_}});    # "reset" hash iterator
            unless(ref($hashVal2) eq 'HASH') {
-               print " (top $cnt)" if($cnt > 0);
+               my $rptLines = keys %{$hashRef->{$_}};
                my $rptCnt = 0;
                $rptCnt += $_ foreach (values %{$hashRef->{$_}});
-               print " (total: $rptCnt)";
+               print " (";
+               print "top $cnt of " if($cnt > 0 && $rptLines > $cnt);
+               print "$rptCnt)";
            }
            print "\n";
            walk_nested_hash($hashRef->{$_}, $cnt, $level);
@@ -1495,16 +1535,16 @@ sub by_domain_then_user {
 # structured to do this here - but it's either that or the callers
 # must run through the hashes twice :-(.
 sub by_count_then_size {
-    ${$hashRef->{$a}}[$msgCntI] = 0 unless(${$hashRef->{$a}}[$msgCntI]);
-    ${$hashRef->{$b}}[$msgCntI] = 0 unless(${$hashRef->{$b}}[$msgCntI]);
-    if(${$hashRef->{$a}}[$msgCntI] == ${$hashRef->{$b}}[$msgCntI]) {
-       ${$hashRef->{$a}}[$msgSizeI] = 0 unless(${$hashRef->{$a}}[$msgSizeI]);
-       ${$hashRef->{$b}}[$msgSizeI] = 0 unless(${$hashRef->{$b}}[$msgSizeI]);
-       return(${$hashRef->{$a}}[$msgSizeI] <=>
-              ${$hashRef->{$b}}[$msgSizeI]);
+    ${$hashRef->{$a}}[MSG_CNT_I] = 0 unless(${$hashRef->{$a}}[MSG_CNT_I]);
+    ${$hashRef->{$b}}[MSG_CNT_I] = 0 unless(${$hashRef->{$b}}[MSG_CNT_I]);
+    if(${$hashRef->{$a}}[MSG_CNT_I] == ${$hashRef->{$b}}[MSG_CNT_I]) {
+       ${$hashRef->{$a}}[MSG_SIZE_I] = 0 unless(${$hashRef->{$a}}[MSG_SIZE_I]);
+       ${$hashRef->{$b}}[MSG_SIZE_I] = 0 unless(${$hashRef->{$b}}[MSG_SIZE_I]);
+       return(${$hashRef->{$a}}[MSG_SIZE_I] <=>
+              ${$hashRef->{$b}}[MSG_SIZE_I]);
     } else {
-       return(${$hashRef->{$a}}[$msgCntI] <=>
-              ${$hashRef->{$b}}[$msgCntI]);
+       return(${$hashRef->{$a}}[MSG_CNT_I] <=>
+              ${$hashRef->{$b}}[MSG_CNT_I]);
     }
 }
 
@@ -1527,37 +1567,36 @@ sub get_datestrs {
 }
 
 # if there's a real domain: uses that.  Otherwise uses the IP addr.
+#
+#   N.B.: in-addr.arpa and ip6.arpa FQDNs return IP addrs
+#
 # Lower-cases returned domain name.
 #
-# Optional bit of code elides the last octet of an IPv4 address.
-# (In case one wants to assume an IPv4 addr. is a dialup or other
-# dynamic IP address in a /24.)
-# Does nothing interesting with IPv6 addresses.
-# FIXME: I think the IPv6 address parsing may be weak
+# Handles IPv4, IPv6, and IPv6-mapped-IPv4 addrs, current-style
+# Postfix bracketed IP addrs, and old-style slash-separated IP addrs
+#
+#   N.B.: IP addr checking is not exhaustive
+#
 sub gimme_domain {
     $_ = $_[0];
-    my($domain, $ipAddr);
-    # split domain/ipaddr into separates
-    # newer versions of Postfix have them "dom.ain[i.p.add.ress]"
-    # older versions of Postfix have them "dom.ain/i.p.add.ress"
-    unless((($domain, $ipAddr) = /^([^\[]*)\[((?:\d{1,3}\.){3}\d{1,3})\]/) == 2||
-           (($domain, $ipAddr) = /^([^\/]*)\/([0-9a-f.:]+)/i) == 2) {
-       # more exhaustive method
-        ($domain, $ipAddr) = /^([^\[\(\/]*)[\[\(\/]([^\]\)]+)[\]\)]:?\d*$/;
+    my $bracketRegex  = '([^\s\[]+)\[((?:\d{1,3}\.){3}\d{1,3}|[\da-fA-F:]+(?:::(?:[\da-fA-F:]+)?)?|[\da-fA-F:]+:(?:\d{1,3}\.){3}\d{1,3})\]';
+    my $slashSepRegex = '([^\s\/]+)\/((?:\d{1,3}\.){3}\d{1,3}|[\da-fA-F:]+(?:::(?:[\da-fA-F:]+)?)?|[\da-fA-F:]+:(?:\d{1,3}\.){3}\d{1,3})';
+    my ($fqdn, $ipaddr);
+
+    print STDERR "dbg: \$_: \"$_\"\n" if(/unknown=0/);
+    unless((($fqdn, $ipaddr) = /$bracketRegex/i) == 2) {
+       ($fqdn, $ipaddr) = /$slashSepRegex/i;
     }
-    # "mach.host.dom"/"mach.host.do.co" to "host.dom"/"host.do.co"
-    if($domain eq "" || $domain eq 'unknown') {
-        $domain = $ipAddr;
-       # For identifying the host part on a Class C network (commonly
-       # seen with dial-ups) the following is handy.
-        # $domain =~ s/\.\d+$//;
+    $fqdn = "unknown" unless($fqdn);
+    $ipaddr = "unknown" unless($ipaddr);
+
+    my $domain;
+    if($fqdn eq "unknown" || $fqdn =~ /\.(in-addr|ip6)\.arpa$/) {
+       $domain = $ipaddr;
     } else {
-        $domain =~
-            s/^(.*)\.([^\.]+)\.([^\.]{3}|[^\.]{2,3}\.[^\.]{2})$/\L$2.$3/;
+       ($domain = $fqdn) =~ s/^(.*)\.([^\.]+)\.([^\.]{3,15}|[^\.]{2,3}\.[^\.]{2})$/\L$2.$3/;
     }
+
     return $domain;
 }
 
@@ -1566,11 +1605,11 @@ sub adj_int_units {
     my $value = $_[0];
     my $units = ' ';
     $value = 0 unless($value);
-    if($value > $divByOneMegAt) {
-       $value /= $oneMeg;
+    if($value > DIV_BY_ONE_MEG_AT) {
+       $value /= ONE_MEG;
        $units = 'm'
-    } elsif($value > $divByOneKAt) {
-       $value /= $oneK;
+    } elsif($value > DIV_BY_ONE_K_AT) {
+       $value /= ONE_K;
        $units = 'k'
     }
     return($value, $units);
@@ -1597,10 +1636,11 @@ sub adj_time_units {
 sub said_string_trimmer {
     my($trimmedString, $maxLen) = @_;
 
+    $trimmedString =~ s/^\d{3}([ -]#?\d\.\d\.\d)? //;
     while(length($trimmedString) > $maxLen) {
-       if($trimmedString =~ /^.* said: /) {
-           $trimmedString =~ s/^.* said: //;
-       } elsif($trimmedString =~ /^.*: */) {
+       if($trimmedString =~ /^.* said:( \d{3}([ -]#?\d\.\d\.\d)?)? /) {
+           $trimmedString =~ s/^.* said:( \d{3}([ -]#?\d\.\d\.\d)?)? //;
+       } elsif($trimmedString =~ /(delivery error|Requested action not taken|Transaction failed|RCPT TO): */) {
            $trimmedString =~ s/^.*?: *//;
        } else {
            $trimmedString = substr($trimmedString, 0, $maxLen - 3) . "...";
@@ -1613,10 +1653,14 @@ sub said_string_trimmer {
 
 # Trim a string, if necessary.  Add elipses to show it.
 sub string_trimmer {
-    my($trimmedString, $maxLen, $doNotTrim) = @_;
+    my($trimmedString, $maxLen) = @_;
 
-    $trimmedString = substr($trimmedString, 0, $maxLen - 3) . "..." 
-       if(! $doNotTrim && (length($trimmedString) > $maxLen));
+    unless($opts{'colWidth'} == 0) {
+        $maxLen += $opts{'colWidth'} - 80 if($opts{'colWidth'} > 0);
+       if(length($trimmedString) > $maxLen) {
+           $trimmedString = substr($trimmedString, 0, $maxLen - 3) . "...";
+       }
+    }
     return $trimmedString;
 }
 
@@ -1635,7 +1679,6 @@ sub proc_smtpd_reject {
     my ($logLine, $rejects, $msgsRjctd, $rejPerHr, $msgsPerDay) = @_;
     my ($rejTyp, $rejFrom, $rejRmdr, $rejReas);
     my ($from, $to);
-    my $rejAddFrom = 0;
 
     ++$$msgsRjctd;
     ++$$rejPerHr;
@@ -1655,16 +1698,19 @@ sub proc_smtpd_reject {
     # Next: get the reject "reason"
     $rejReas = $rejRmdr;
     unless(defined($opts{'verbMsgDetail'})) {
-       if($rejTyp eq "RCPT" || $rejTyp eq "DATA" || $rejTyp eq "CONNECT") {    # special treatment :-(
+       if($rejTyp eq "RCPT" || $rejTyp eq "DATA" || $rejTyp eq "CONNECT" || $rejTyp eq "BDAT") {       # special treatment :-(
            # If there are "<>"s immediately following the reject code, that's
            # an email address or HELO string.  There can be *anything* in
            # those--incl. stuff that'll screw up subsequent parsing.  So just
            # get rid of it right off.
-           $rejReas =~ s/^(\d{3} <).*?(>:)/$1$2/;
-           $rejReas =~ s/^(?:\d{3} \d\.\d\.\d )(Protocol error);.*$/$1/;
-           $rejReas =~ s/^(?:.*?[:;] )(?:\[[^\]]+\] )?([^;,]+)[;,].*$/$1/;
+           $rejReas =~ s/^(\d{3} (?:\d\.\d\.\d )?<).*?(>:)/$1$2/;
+           $rejReas =~ s/^(?:\d{3} \d\.\d\.\d )(Protocol error);.*$/$1/;               # from Deb 1.1.15-8
+           $rejReas =~ s/^.*?[:;] (?:\[[^\]]+\] )?([^;,]+)[;,].*$/$1/;
            $rejReas =~ s/^((?:Sender|Recipient) address rejected: [^:]+):.*$/$1/;
-           $rejReas =~ s/(client|Client host|Sender address) .+? blocked/blocked/;
+           $rejReas =~ s/(client|Client host|Sender address) .+? blocked/blocked/;     # from Deb 1.1.15-8
+           $rejReas =~ s/^\d{3} \d\.\d\.\d (Server configuration (?:error|problem));.+$/$1/;
+           # Condense fqrdns.pcre reports
+           $rejReas =~ s/^Unverified (Client host rejected: Generic - Please relay via ISP).*$/$1/;
        } elsif($rejTyp eq "MAIL") {    # *more* special treatment :-( grrrr...
            $rejReas =~ s/^\d{3} (?:<.+>: )?([^;:]+)[;:]?.*$/$1/;
        } else {
@@ -1686,8 +1732,8 @@ sub proc_smtpd_reject {
     (($from) = $rejRmdr =~ /from=<([^>]+)>/) || ($from = "<>");
 
     if(defined($from)) {
-       $rejAddFrom = $opts{'rejAddFrom'};
        $from = verp_mung($from);
+       $from = srs_mung($from);
        $from = lc($from) if($opts{'i'});
     }
 
@@ -1695,14 +1741,16 @@ sub proc_smtpd_reject {
     if($rejReas =~ m/^Sender address rejected:/) {
        # Sender address rejected: Domain not found
        # Sender address rejected: need fully-qualified address
-       ++$rejects->{$rejTyp}{$rejReas}{$from};
+       my $rejData = $from;
+       $rejData .= "  ($to)" if($opts{'rejAddTo'} && $to);
+       ++$rejects->{$rejTyp}{$rejReas}{$rejData};
     } elsif($rejReas =~ m/^(Recipient address rejected:|User unknown( |$))/) {
        # Recipient address rejected: Domain not found
        # Recipient address rejected: need fully-qualified address
        # User unknown (in local/relay recipient table)
        #++$rejects->{$rejTyp}{$rejReas}{$to};
        my $rejData = $to;
-       if($rejAddFrom) {
+       if($opts{'rejAddFrom'}) {
            $rejData .= "  (" . ($from? $from : gimme_domain($rejFrom)) . ")";
        }
        ++$rejects->{$rejTyp}{$rejReas}{$rejData};
@@ -1712,16 +1760,22 @@ sub proc_smtpd_reject {
        ++$rejects->{$rejTyp}{$rejReas}{$src};
     } elsif($rejReas =~ s/^.*?\d{3} (Message size exceeds fixed limit);.*$/$1/) {
        my $rejData = gimme_domain($rejFrom);
-       $rejData .= "  ($from)" if($rejAddFrom);
+       $rejData .= "  ($from)" if($opts{'rejAddFrom'});
        ++$rejects->{$rejTyp}{$rejReas}{$rejData};
     } elsif($rejReas =~ s/^.*?\d{3} (Server configuration (?:error|problem));.*$/(Local) $1/) {
        my $rejData = gimme_domain($rejFrom);
-       $rejData .= "  ($from)" if($rejAddFrom);
+       $rejData .= "  ($from)" if($opts{'rejAddFrom'});
        ++$rejects->{$rejTyp}{$rejReas}{$rejData};
     } else {
 #      print STDERR "dbg: unknown reject reason $rejReas !\n\n";
        my $rejData = gimme_domain($rejFrom);
-       $rejData .= "  ($from)" if($rejAddFrom);
+       if($opts{'rejAddFrom'} && $opts{'rejAddTo'} && $to) {
+           $rejData .= "  ($from -> $to)";
+       } elsif($opts{'rejAddFrom'}) {
+           $rejData .= "  (< $from)";
+       } elsif($opts{'rejAddTo'} && $to) {
+           $rejData .= "  (> $to)";
+       }
        ++$rejects->{$rejTyp}{$rejReas}{$rejData};
     }
 }
@@ -1746,6 +1800,16 @@ sub verp_mung {
     return $addr;
 }
 
+sub srs_mung {
+    my $addr = $_[0];
+
+    if(defined($opts{'srsMung'})) {
+       $addr =~ s/^SRS(?:[01])(?:[=+-])(?:[^=]+=[\w\.]+==)*(?:[^=]+=[^=]+=)([\w\.]+)=(.+)@[\w\.]+$/$2\@$1/i;
+    }
+
+    return $addr;
+}
+
 ###
 ### Warning and Error Routines
 ###
index c22cf4bbbd3a7c349f830c6f3b30044f1a2ecefe..1b8dbefa388684cc73de8f5c11e8e62432bb429d 100644 (file)
@@ -55,7 +55,7 @@
 .\" ========================================================================
 .\"
 .IX Title "PFLOGSUMM 1"
-.TH PFLOGSUMM 1 2025-05-22 1.1.6 "User Contributed Perl Documentation"
+.TH PFLOGSUMM 1 2025-05-25 1.1.8 "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
 .SH NAME
 pflogsumm \- Produce Postfix MTA logfile summary
 .PP
-Copyright (C) 1998\-2025 by James S. Seymour, Release 1.1.6
+Copyright (C) 1998\-2025 by James S. Seymour, Release 1.1.8
 .SH SYNOPSIS
 .IX Header "SYNOPSIS"
 .Vb 8
 \&    pflogsumm \-[eq] [\-d <today|yesterday>] [\-\-detail <cnt>]
-\&        [\-\-bounce\-detail <cnt>] [\-\-deferral\-detail <cnt>]
+\&        [\-\-bounce\-detail <cnt>] [\-\-colwidth <n>] [\-\-deferral\-detail <cnt>]
 \&        [\-h <cnt>] [\-i|\-\-ignore\-case] [\-\-iso\-date\-time] [\-\-mailq]
-\&        [\-m|\-\-uucp\-mung] [\-\-no\-no\-msg\-size] [\-\-problems\-first]
-\&        [\-\-rej\-add\-from] [\-\-reject\-detail <cnt>] [\-\-smtp\-detail <cnt>]
-\&        [\-\-smtpd\-stats] [\-\-smtpd\-warning\-detail <cnt>]
-\&        [\-\-syslog\-name=string] [\-u <cnt>] [\-\-verbose\-msg\-detail]
-\&        [\-\-verp\-mung[=<n>]] [\-\-zero\-fill] [file1 [filen]]
+\&        [\-m|\-\-uucp\-mung] [\-\-no\-no\-msg\-size] [\-\-problems\-first] [\-\-rej\-add\-from]
+\&        [\-\-rej\-add\-to] [\-\-reject\-detail <cnt>] [\-\-smtp\-detail <cnt>]
+\&        [\-\-smtpd\-stats] [\-\-smtpd\-warning\-detail <cnt>] [\-\-srs\-mung]
+\&        [\-\-syslog\-name=string] [\-u <cnt>] [\-\-use\-orig\-to]
+\&        [\-\-verbose\-msg\-detail] [\-\-verp\-mung[=<n>] [\-\-zero\-fill] [file1 [filen]]
 \&
 \&    pflogsumm \-[help|version]
 \&
@@ -100,6 +100,13 @@ Copyright (C) 1998\-2025 by James S. Seymour, Release 1.1.6
 \&                   Limit detailed bounce reports to the top <cnt>.  0
 \&                   to suppress entirely.
 \&
+\&    \-\-colwidth <n>
+\&
+\&                    Maximum report output width.  Default is 80 columns.
+\&                    0 = unlimited.
+\&
+\&                    N.B.: \-\-verbose\-msg\-detail overrides
+\&
 \&    \-d today       generate report for just today
 \&    \-d yesterday   generate report for just "yesterday"
 \&
@@ -173,24 +180,6 @@ Copyright (C) 1998\-2025 by James S. Seymour, Release 1.1.6
 \&                   is in $PATH.  See "$mailqCmd" variable to path thisi
 \&                   if desired.)
 \&
-\&    \-\-no_bounce_detail
-\&    \-\-no_deferral_detail
-\&    \-\-no_reject_detail
-\&
-\&                   These switches are deprecated in favour of
-\&                   \-\-bounce\-detail, \-\-deferral\-detail and
-\&                   \-\-reject\-detail, respectively.
-\&
-\&                   Suppresses the printing of the following detailed
-\&                   reports, respectively:
-\&
-\&                        message bounce detail (by relay)
-\&                        message deferral detail
-\&                        message reject detail
-\&
-\&                   See also: "\-u" and "\-h" for further report\-limiting
-\&                             options.
-\&
 \&    \-\-no\-no\-msg\-size
 \&
 \&                    Do not emit report on "Messages with no size data".
@@ -204,15 +193,6 @@ Copyright (C) 1998\-2025 by James S. Seymour, Release 1.1.6
 \&                    normally reported by pflogsumm as "Messages with no
 \&                    size data."
 \&
-\&    \-\-no\-smtpd\-warnings
-\&
-\&                   This switch is deprecated in favour of
-\&                   smtpd\-warning\-detail
-\&
-\&                    On a busy mail server, say at an ISP, SMTPD warnings
-\&                    can result in a rather sizeable report.  This option
-\&                    turns reporting them off.
-\&
 \&    \-\-problems\-first
 \&
 \&                   Emit "problems" reports (bounces, defers, warnings,
@@ -229,6 +209,11 @@ Copyright (C) 1998\-2025 by James S. Seymour, Release 1.1.6
 \&                   note: headings for warning, fatal, and "master"
 \&                   messages will always be printed.
 \&
+\&    \-\-rej\-add\-to
+\&
+\&                   For sender reject reports: Add the intended recipient
+\&                   address.
+\&
 \&    \-\-reject\-detail <cnt>
 \&
 \&                   Limit detailed smtpd reject, warn, hold and discard
@@ -252,6 +237,23 @@ Copyright (C) 1998\-2025 by James S. Seymour, Release 1.1.6
 \&                   Limit detailed smtpd warnings reports to the top <cnt>.
 \&                   0 to suppress entirely.
 \&
+\&    \-\-srs\-mung
+\&
+\&                   Undo SRS address munging.
+\&
+\&                   If your postfix install has an SRS plugin running, many
+\&                   addresses in the report will contain the SRS\-formatted
+\&                   email addresses, also for non\-local adresses (f.i.
+\&                   senders). This option will try to undo the "damage".
+\&
+\&                   Addresses of the form:
+\&
+\&                     SRS0=A6cv=PT=sender.example.com=support@srs.example.net
+\&
+\&                   will be reformatted to their original value:
+\&
+\&                     support@sender.example.com
+\&
 \&    \-\-syslog\-name=name
 \&
 \&                   Set syslog\-name to look for for Postfix log entries.
@@ -270,6 +272,11 @@ Copyright (C) 1998\-2025 by James S. Seymour, Release 1.1.6
 \&                   See also: "\-h" and "\-\-*\-detail" options for further
 \&                             report\-limiting options.
 \&
+\&    \-\-use\-orig\-to
+\&
+\&                  Where "orig_to" fields are found, report that in place
+\&                  of the "to" address.
+\&
 \&    \-\-verbose\-msg\-detail
 \&
 \&                   For the message deferral, bounce and reject summaries:
@@ -347,6 +354,8 @@ Copyright (C) 1998\-2025 by James S. Seymour, Release 1.1.6
 .SH "SEE ALSO"
 .IX Header "SEE ALSO"
 .Vb 1
+\&    pffrombyto, pftobyfrom
+\&
 \&    The pflogsumm FAQ: pflogsumm\-faq.txt.
 .Ve
 .SH NOTES
index 986515e2be89a1645c42af6a7ef0819d575cfa95..b41e649c94d59f97fd961c5e98e422c4eb6f52fb 100644 (file)
@@ -55,7 +55,7 @@
 .\" ========================================================================
 .\"
 .IX Title "PFTOBYFROM 1"
-.TH PFTOBYFROM 1 2025-05-22 1.1.6 "User Contributed Perl Documentation"
+.TH PFTOBYFROM 1 2025-05-22 1.1.8 "User Contributed Perl Documentation"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l