summaryrefslogtreecommitdiff
path: root/postfix
diff options
context:
space:
mode:
authorTassilo Erlewein <tassilo.erlewein@erfrakon.de>2002-11-12 23:03:21 (GMT)
committerTassilo Erlewein <tassilo.erlewein@erfrakon.de>2002-11-12 23:03:21 (GMT)
commit0bbdd447b06c642b625b3787356f3826882bfec0 (patch)
tree2c0c42071b5329cefa5a4b12cbde2d89362f2bc9 /postfix
parentebbaee7f90fcb40458edcc874086a7d22791acb6 (diff)
downloadserver-0bbdd447b06c642b625b3787356f3826882bfec0.tar.gz
*** empty log message ***
Diffstat (limited to 'postfix')
-rw-r--r--postfix/Makefile5
-rw-r--r--postfix/etc.tarbin0 -> 20480 bytes
-rw-r--r--postfix/fsl.postfix16
-rw-r--r--postfix/pflogsumm-1.0.4.pl1289
-rw-r--r--postfix/postfix-db4.patch16
-rw-r--r--postfix/postfix-sasl2.patch715
-rw-r--r--postfix/postfix.spec336
-rw-r--r--postfix/rc.postfix60
8 files changed, 2437 insertions, 0 deletions
diff --git a/postfix/Makefile b/postfix/Makefile
new file mode 100644
index 0000000..b68419d
--- /dev/null
+++ b/postfix/Makefile
@@ -0,0 +1,5 @@
+all:
+ rpm -bb postfix.spec --define 'with_sasl yes' --define 'with_fsl yes'
+
+nofsl:
+ rpm -bb postfix.spec --define 'with_sasl yes'
diff --git a/postfix/etc.tar b/postfix/etc.tar
new file mode 100644
index 0000000..c23cb44
--- /dev/null
+++ b/postfix/etc.tar
Binary files differ
diff --git a/postfix/fsl.postfix b/postfix/fsl.postfix
new file mode 100644
index 0000000..f036db5
--- /dev/null
+++ b/postfix/fsl.postfix
@@ -0,0 +1,16 @@
+##
+## fsl.postfix -- OSSP fsl configuration for Postfix
+##
+
+ident (postfix/.+)/.+ q{
+ prefix(
+ prefix="%b %d %H:%M:%S %N <%L> $1[%P]: "
+ )
+ -> {
+ debug: file(
+ path="@l_prefix@/var/postfix/log/postfix.log",
+ append=1, perm=0644
+ )
+ }
+}
+
diff --git a/postfix/pflogsumm-1.0.4.pl b/postfix/pflogsumm-1.0.4.pl
new file mode 100644
index 0000000..68a4ae9
--- /dev/null
+++ b/postfix/pflogsumm-1.0.4.pl
@@ -0,0 +1,1289 @@
+#!/usr/bin/perl -w
+eval 'exec perl -S $0 "$@"'
+ if 0;
+#
+# pflogsumm.pl - Produce summaries of Postfix/VMailer MTA in logfile -
+# Copyright (C) 1998-2002 by James S. Seymour (jseymour@LinxNet.com)
+# (See "License", below.) Release 1.0.4.
+#
+# Usage:
+# pflogsumm.pl -[eq] [-d <today|yesterday>] [-h <cnt>] [-u <cnt>]
+# [--verp_mung[=<n>]] [--verbose_msg_detail] [--iso_date_time]
+# [-m|--uucp_mung] [-i|--ignore_case] [--smtpd_stats] [--mailq]
+# [--problems_first] [--rej_add_from] [--no_bounce_detail]
+# [--no_deferral_detail] [--no_reject_detail] [file1 [filen]]
+#
+# pflogsumm.pl -[help|version]
+#
+# Options:
+#
+# -d today means just today
+# -d yesterday means just "yesterday"
+#
+# -e extended (extreme? excessive?) detail - emit detailed
+# reports. At present, this includes only a per-message
+# report, sorted by sender domain, then user-in-domain,
+# then by queue i.d.
+#
+# WARNING: the data built to generate this report can
+# quickly consume very large amounts of memory if a lot
+# of log entries are processed!
+#
+# -h <cnt> top <cnt> to display in host/domain reports. 0 == none.
+#
+# See also: "-u" and "--no_*_detail" for further report-
+# limiting options.
+#
+# --help Emit short usage message and bail out. (By happy
+# coincidence, "-h" alone does much the same, being as
+# it requires a numeric argument :-). Yeah, I know:
+# lame.)
+#
+# -i
+# --ignore_case Handle complete email address in a case-insensitive
+# manner. Normally pflogsumm lower-cases only the
+# host and domain parts, leaving the user part alone.
+# This option causes the entire email address to be
+# lower-cased.
+#
+# --iso_date_time
+#
+# For summaries that contain date or time information, use
+# ISO 8601 standard formats (CCYY-MM-DD and HH:MM), rather
+# than "Mon DD CCYY" and "HHMM".
+#
+# -m modify (mung?) UUCP-style bang-paths
+# --uucp_mung
+#
+# This is for use when you have a mix of Internet-style
+# domain addresses and UUCP-style bang-paths in the log.
+# Upstream UUCP feeds sometimes mung Internet domain
+# style address into bang-paths. This option can
+# sometimes undo the "damage". For example:
+# "somehost.dom!username@foo" (where "foo" is the next
+# host upstream and "somehost.dom" was whence the email
+# originated) will get converted to
+# "foo!username@somehost.dom". This also affects the
+# extended detail report (-e), to help ensure that by-
+# domain-by-name sorting is more accurate.
+#
+# --mailq Run "mailq" command at end of report. Merely a
+# convenience feature. (Assumes that "mailq" is in
+# $PATH. See "$mailqCmd" variable to path this if
+# desired.)
+#
+# --no_bounce_detail
+# --no_deferral_detail
+# --no_reject_detail
+#
+# 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.
+#
+# --problems_first
+#
+# Emit "problems" reports (bounces, defers, warnings, etc.)
+# before "normal" stats.
+#
+# --rej_add_from
+# For those reject reports that list IP addresses or
+# host/domain names: append the email from address to
+# each listing. (Does not apply to "Improper use of SMTP
+# command pipelining" report.)
+#
+# -q quiet - don't print headings for empty reports (note:
+# headings for warning, fatal, and "master" messages will
+# always be printed.)
+#
+# --smtpd_stats
+#
+# Generate smtpd connection statistics.
+#
+# The "per-day" report is not generated for single-day
+# reports. For multiple-day reports: "per-hour" numbers
+# are daily averages (reflected in the report heading).
+#
+# -u <cnt> top <cnt> to display in user reports. 0 == none.
+#
+# See also: "-h" and "--no_*_detail" for further report-
+# limiting options.
+#
+# --verbose_msg_detail
+#
+# For the message deferral, bounce and reject summaries:
+# display the full "reason", rather than a truncated one.
+# Note: this can result in quite long lines in the report.
+#
+# --verp_mung do "VERP" generated address (?) munging. Convert
+# --verp_mung=2 sender addresses of the form
+# "list-return-NN-someuser=some.dom@host.sender.dom"
+# to
+# "list-return-ID-someuser=some.dom@host.sender.dom"
+#
+# In other words: replace the numeric value with "ID".
+#
+# By specifying the optional "=2" (second form), the
+# munging is more "aggressive", converting the address
+# to something like:
+#
+# "list-return@host.sender.dom"
+#
+# (Actually: specifying anything less than 2 does the
+# "simple" munging and anything greater than 1 results
+# in the more "aggressive" hack being applied.)
+#
+# --version Print program name and version and bail out.
+#
+# If no file(s) specified, reads from stdin. Output is to stdout.
+#
+# Typical usage:
+# Produce a report of previous day's activities:
+# pflogsumm.pl -d yesterday /var/log/syslog
+# A report of prior week's activities (after logs rotated):
+# pflogsumm.pl /var/log/syslog.1
+# What's happened so far today:
+# pflogsumm.pl -d today /var/log/syslog
+#
+# Notes:
+#
+# -------------------------------------------------------------
+# IMPORTANT: Pflogsumm makes no attempt to catch/parse non-
+# postfix/vmailer daemon log entries. (I.e.: Unless
+# it has "postfix/" or "vmailer/" in the log entry,
+# it will be ignored.)
+# -------------------------------------------------------------
+#
+# The "-c <cnt>" option is gone. Use "-h <cnt>" and/or "-u <cnt>"
+# instead.
+#
+# For display purposes: integer values are munged into "kilo" and
+# "mega" notation as they exceed certain values. I chose the
+# admittedly arbitrary boundaries of 512k and 512m as the points
+# at which to do this--my thinking being 512x was the largest
+# number (of digits) that most folks can comfortably grok
+# at-a-glance. These are "computer" "k" and "m", not 1000 and
+# 1,000,000. You can easily change all of this with some
+# constants near the beginning of the program.
+#
+# "Items-per-day" reports are not generated for single-day
+# reports. For multiple-day reports: "Items-per-hour" numbers
+# are daily averages (reflected in the report headings).
+#
+# It's important that the logs are presented to pflogsumm in
+# chronological order so that message sizes are available when
+# needed.
+#
+# License:
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You may have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+# USA.
+#
+# An on-line copy of the GNU General Public License can be found
+# http://www.fsf.org/copyleft/gpl.html.
+#
+# Pflogsumm requires the Date::Calc module, which can be obtained from
+# CPAN at http://www.perl.com.
+#
+# The Pflogsumm Home Page is at:
+#
+# http://jimsun.LinxNet.com/postfix_contrib.html
+#
+
+use strict;
+use locale;
+use Getopt::Long;
+# ---Begin: SMTPD_STATS_SUPPORT---
+use Date::Calc qw(Delta_DHMS);
+# ---End: SMTPD_STATS_SUPPORT---
+
+my $mailqCmd = "mailq";
+my $release = "1.0.4";
+
+# 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
+
+# Constants used throughout pflogsumm
+@monthNames = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+%monthNums = qw(
+ Jan 0 Feb 1 Mar 2 Apr 3 May 4 Jun 5
+ Jul 6 Aug 7 Sep 8 Oct 9 Nov 10 Dec 11);
+($thisMon, $thisYr) = (localtime(time()))[4,5];
+$thisYr += 1900;
+
+#
+# Variables used only in main loop
+#
+# Per-user data
+my (%recipUser, $recipUserCnt);
+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
+
+my (
+ $cmd, $qid, $addr, $size, $relay, $status, $delay,
+ $dateStr,
+ %panics, %fatals, %warnings, %masterMsgs,
+ %msgSizes,
+ %deferred, %bounced,
+ %noMsgSize, %msgDetail,
+ $msgsRcvd, $msgsDlvrd, $sizeRcvd, $sizeDlvrd,
+ $msgMonStr, $msgMon, $msgDay, $msgTimeStr, $msgHr, $msgMin, $msgSec,
+ $msgYr,
+ $revMsgDateStr, $dayCnt, %msgsPerDay,
+ %rejects, $msgsRjctd,
+ %rcvdMsg, $msgsFwdd, $msgsBncd,
+ $msgsDfrdCnt, $msgsDfrd, %msgDfrdFlgs,
+ %connTime, %smtpdPerDay, %smtpdPerDom, $smtpdConnCnt, $smtpdTotTime,
+ %smtpMsgs
+);
+$dayCnt = $smtpdConnCnt = $smtpdTotTime = 0;
+
+# Messages received and delivered per hour
+my @rcvPerHr = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
+my @dlvPerHr = @rcvPerHr;
+my @dfrPerHr = @rcvPerHr; # defers per hour
+my @bncPerHr = @rcvPerHr; # bounces per hour
+my @rejPerHr = @rcvPerHr; # rejects per hour
+my $lastMsgDay = 0;
+
+# "doubly-sub-scripted array: cnt, total and max time per-hour
+# Gag - some things, Perl doesn't do well :-(
+my @smtpdPerHr;
+$smtpdPerHr[0] = [0,0,0]; $smtpdPerHr[1] = [0,0,0]; $smtpdPerHr[2] = [0,0,0];
+$smtpdPerHr[3] = [0,0,0]; $smtpdPerHr[4] = [0,0,0]; $smtpdPerHr[5] = [0,0,0];
+$smtpdPerHr[6] = [0,0,0]; $smtpdPerHr[7] = [0,0,0]; $smtpdPerHr[8] = [0,0,0];
+$smtpdPerHr[9] = [0,0,0]; $smtpdPerHr[10] = [0,0,0]; $smtpdPerHr[11] = [0,0,0];
+$smtpdPerHr[12] = [0,0,0]; $smtpdPerHr[13] = [0,0,0]; $smtpdPerHr[14] = [0,0,0];
+$smtpdPerHr[15] = [0,0,0]; $smtpdPerHr[16] = [0,0,0]; $smtpdPerHr[17] = [0,0,0];
+$smtpdPerHr[18] = [0,0,0]; $smtpdPerHr[19] = [0,0,0]; $smtpdPerHr[20] = [0,0,0];
+$smtpdPerHr[21] = [0,0,0]; $smtpdPerHr[22] = [0,0,0]; $smtpdPerHr[23] = [0,0,0];
+
+$progName = "pflogsumm.pl";
+$usageMsg =
+ "usage: $progName -[eq] [-d <today|yesterday>] [-h <cnt>] [-u <cnt>]
+ [--verp_mung[=<n>]] [--verbose_msg_detail] [--iso_date_time]
+ [-m|--uucp_mung] [-i|--ignore_case] [--smtpd_stats] [--mailq]
+ [--problems_first] [--rej_add_from] [--no_bounce_detail]
+ [--no_deferral_detail] [--no_reject_detail] [file1 [filen]]
+
+ $progName --[version|help]";
+
+# Some pre-inits for convenience
+$isoDateTime = 0; # Don't use ISO date/time formats
+GetOptions(
+ "d=s" => \$opts{'d'},
+ "e" => \$opts{'e'},
+ "help" => \$opts{'help'},
+ "h=i" => \$opts{'h'},
+ "i" => \$opts{'i'},
+ "ignore_case" => \$opts{'i'},
+ "iso_date_time" => \$isoDateTime,
+ "m" => \$opts{'m'},
+ "uucp_mung" => \$opts{'m'},
+ "mailq" => \$opts{'mailq'},
+ "no_bounce_detail" => \$opts{'noBounceDetail'},
+ "no_deferral_detail" => \$opts{'noDeferralDetail'},
+ "no_reject_detail" => \$opts{'noRejectDetail'},
+ "problems_first" => \$opts{'pf'},
+ "q" => \$opts{'q'},
+ "rej_add_from" => \$opts{'rejAddFrom'},
+ "smtpd_stats" => \$opts{'smtpdStats'},
+ "u=i" => \$opts{'u'},
+ "verbose_msg_detail" => \$opts{'verbMsgDetail'},
+ "verp_mung:i" => \$opts{'verpMung'},
+ "version" => \$opts{'version'}
+) || die "$usageMsg\n";
+
+# internally: 0 == none, undefined == -1 == all
+$opts{'h'} = -1 unless(defined($opts{'h'}));
+$opts{'u'} = -1 unless(defined($opts{'u'}));
+
+if(defined($opts{'help'})) {
+ print "$usageMsg\n";
+ exit 0;
+}
+
+if(defined($opts{'version'})) {
+ print "$progName $release\n";
+ exit 0;
+}
+
+$dateStr = get_datestr($opts{'d'}) if(defined($opts{'d'}));
+
+# debugging
+#open(UNPROCD, "> unprocessed") ||
+# die "couldn't open \"unprocessed\": $!\n";
+
+while(<>) {
+ next if(defined($dateStr) && ! /^$dateStr/o);
+ s/: \[ID [0-9]+\s+[^\]]+\] /: /o; # lose "[ID nnnnnn some.thing]" stuff
+ ($msgMonStr, $msgDay, $msgTimeStr, $cmd, $qid) =
+ m#^(...)\s+([0-9]+)\s(..:..:..)\s.*?(?:vmailer|postfix)[-/]([^\[:]*).*?: ([^:]+)#o;
+ ($msgMonStr, $msgDay, $msgTimeStr, $cmd, $qid) =
+ m#^(...)\s+([0-9]+)\s(..:..:..)\s.*?(vmailer|postfix[^\[:]*).*?: ([^:]+)#o unless($cmd);
+ next unless($cmd);
+ chomp;
+
+ # snatch out log entry date & time
+ ($msgHr, $msgMin, $msgSec) = split(/:/, $msgTimeStr);
+ $msgMon = $monthNums{$msgMonStr};
+ $msgYr = $thisYr; --$msgYr if($msgMon > $thisMon);
+
+ # the following test depends on one getting more than one message a
+ # month--or at least that successive messages don't arrive on the
+ # same month-day in successive months :-)
+ unless($msgDay == $lastMsgDay) {
+ $lastMsgDay = $msgDay;
+ $revMsgDateStr = sprintf "%d%02d%02d", $msgYr, $msgMon, $msgDay;
+ ++$dayCnt;
+ }
+
+ # regexp rejects happen in "cleanup"
+ if(my($rejTyp, $rejReas, $rejRmdr) =
+ /^.*\/(cleanup)\[.*reject: ([^\s]+) (.*)$/o)
+ {
+ $rejRmdr =~ s/; from=<.*$//o unless($opts{'verbMsgDetail'});
+ $rejRmdr = string_trimmer($rejRmdr, 64, $opts{'verbMsgDetail'});
+ ++$rejects{$rejTyp}{$rejReas}{$rejRmdr};
+ ++$msgsRjctd;
+ ++$rejPerHr[$msgHr];
+ ++${$msgsPerDay{$revMsgDateStr}}[4];
+ } elsif($qid eq 'warning') {
+ (my $warnReas = $_) =~ s/^.*warning: //o;
+ $warnReas = string_trimmer($warnReas, 66, $opts{'verbMsgDetail'});
+ ++$warnings{$cmd}{$warnReas};
+ } elsif($qid eq 'fatal') {
+ (my $fatalReas = $_) =~ s/^.*fatal: //o;
+ $fatalReas = string_trimmer($fatalReas, 66, $opts{'verbMsgDetail'});
+ ++$fatals{$cmd}{$fatalReas};
+ } elsif($qid eq 'panic') {
+ (my $panicReas = $_) =~ s/^.*panic: //o;
+ $panicReas = string_trimmer($panicReas, 66, $opts{'verbMsgDetail'});
+ ++$panics{$cmd}{$panicReas};
+ } elsif($qid eq 'reject') {
+ # This could get real ugly!
+ # First: get everything following the "reject: " token
+ my $rejFrom;
+ ($rejTyp, $rejFrom, $rejRmdr) =
+ /^.* reject: ([^ ]+) from ([^:]+): (.*)$/o;
+ # Next: get the reject "reason"
+ $rejReas = $rejRmdr;
+ unless(defined($opts{'verbMsgDetail'})) {
+ if($rejTyp eq "RCPT") { # special treatment :-(
+ $rejReas =~ s/^(?:.*?[:;] )(?:\[[^\]]+\] )?([^;,]+)[;,].*$/$1/oi;
+ } else {
+ $rejReas =~ s/^(?:.*[:;] )?([^,]+).*$/$1/o;
+ }
+ }
+ # stash in "triple-subscripted-array"
+
+ my ($from, $to) = $rejRmdr =~ m/from=<(.*?)>\s+to=<(.*?)>/;
+ if($rejReas =~ m/^Sender address rejected:/o) {
+ # Sender address rejected: Domain not found
+ # Sender address rejected: need fully-qualified address
+ ++$rejects{$rejTyp}{$rejReas}{$from};
+ } elsif($rejReas =~ m/^Recipient address rejected:/o) {
+ # Recipient address rejected: Domain not found
+ # Recipient address rejected: need fully-qualified address
+ ++$rejects{$rejTyp}{$rejReas}{$to};
+ } elsif($rejReas =~ s/^.*?\d{3} (Improper use of SMTP command pipelining);.*$/$1/o) {
+ my ($src) = /^.+? from ([^:]+):.*$/o;
+ ++$rejects{$rejTyp}{$rejReas}{$src};
+ } else {
+# print STDERR "dbg: unknown reject reason $rejReas !\n\n";
+ my $rejData = gimme_domain($rejFrom);
+ $rejData .= " ($from)" if($opts{'rejAddFrom'});
+ ++$rejects{$rejTyp}{$rejReas}{$rejData};
+ }
+ ++$msgsRjctd;
+ ++$rejPerHr[$msgHr];
+ ++${$msgsPerDay{$revMsgDateStr}}[4];
+ } elsif($cmd eq 'master') {
+ ++$masterMsgs{(split(/^.*master.*: /))[1]};
+ } elsif($cmd eq 'smtpd') {
+ if(/: client=/o) {
+ #
+ # Warning: this code in two places!
+ #
+ ++$rcvPerHr[$msgHr];
+ ++${$msgsPerDay{$revMsgDateStr}}[0];
+ ++$msgsRcvd;
+ ++$rcvdMsg{$qid}; # quick-set a flag
+ }
+# ---Begin: SMTPD_STATS_SUPPORT---
+ else {
+ next unless(defined($opts{'smtpdStats'}));
+ if(/: connect from /o) {
+ /\/smtpd\[([0-9]+)\]: /o;
+ @{$connTime{$1}} =
+ ($msgYr, $msgMon + 1, $msgDay, $msgHr, $msgMin, $msgSec);
+ } elsif(/: disconnect from /o) {
+ my ($pid, $hostID) = /\/smtpd\[([0-9]+)\]: disconnect from (.+)$/o;
+ 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
+ my $tSecs = (86400 * $d) + (3600 * $h) + (60 * $m) + $s;
+
+ ++$smtpdPerHr[$msgHr][0];
+ $smtpdPerHr[$msgHr][1] += $tSecs;
+ $smtpdPerHr[$msgHr][2] = $tSecs if($tSecs > $smtpdPerHr[$msgHr][2]);
+
+ unless(${$smtpdPerDay{$revMsgDateStr}}[0]++) {
+ ${$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;
+ }
+ ${$smtpdPerDom{$hostID}}[1] += $tSecs;
+ ${$smtpdPerDom{$hostID}}[2] = $tSecs
+ if($tSecs > ${$smtpdPerDom{$hostID}}[2]);
+
+ ++$smtpdConnCnt;
+ $smtpdTotTime += $tSecs;
+ }
+ }
+ }
+# ---End: SMTPD_STATS_SUPPORT---
+ } else {
+ my $toRmdr;
+ if((($addr, $size) = /from=<([^>]*)>, size=([0-9]+)/o) == 2)
+ {
+ next if($msgSizes{$qid}); # avoid double-counting!
+ if($addr) {
+ if($opts{'m'} && $addr =~ /^(.*!)*([^!]+)!([^!@]+)@([^\.]+)$/o) {
+ $addr = "$4!" . ($1? "$1" : "") . $3 . "\@$2";
+ }
+ $addr =~ s/(@.+)/\L$1/o unless($opts{'i'});
+ $addr = lc($addr) if($opts{'i'});
+
+ # Hack for VERP (?) - convert address from somthing like
+ # "list-return-36-someuser=someplace.com@lists.domain.com"
+ # to "list-return-ID-someuser=someplace.com@lists.domain.com"
+ # to prevent per-user listing "pollution." More aggressive
+ # munging converts to something like
+ # "list-return@lists.domain.com" (Instead of "return," there
+ # may be numeric list name/id, "warn", "error", etc.?)
+ if(defined($opts{'verpMung'})) {
+ if($opts{'verpMung'} > 1) {
+# $addr =~ s/^(.+)-return-\d+-[^\@]+(\@.+)$/$1$2/o;
+ $addr =~ s/-(\d+-)?[^=-]+=[^\@]+\@/\@/o;
+ } else {
+# $addr =~ s/-return-\d+-/-return-ID-/o;
+ $addr =~ s/-(return|\d+)-\d+-/-$1-ID-/o;
+ }
+ }
+ } else {
+ $addr = "from=<>"
+ }
+ $msgSizes{$qid} = $size;
+ push(@{$msgDetail{$qid}}, $addr) if($opts{'e'});
+ # Avoid counting forwards
+ if($rcvdMsg{$qid}) {
+ (my $domAddr = $addr) =~ s/^[^@]+\@//o; # get domain only
+ ++$sendgDomCnt
+ unless(${$sendgDom{$domAddr}}[$msgCntI]);
+ ++${$sendgDom{$domAddr}}[$msgCntI];
+ ${$sendgDom{$domAddr}}[$msgSizeI] += $size;
+ ++$sendgUserCnt unless(${$sendgUser{$addr}}[$msgCntI]);
+ ++${$sendgUser{$addr}}[$msgCntI];
+ ${$sendgUser{$addr}}[$msgSizeI] += $size;
+ $sizeRcvd += $size;
+ delete($rcvdMsg{$qid}); # limit hash size
+ }
+ }
+ elsif((($addr, $relay, $delay, $status, $toRmdr) =
+ /to=<([^>]*)>, relay=([^,]+), delay=([^,]+), status=([^ ]+)(.*)$/o) >= 4)
+ {
+
+ if($opts{'m'} && $addr =~ /^(.*!)*([^!]+)!([^!@]+)@([^\.]+)$/o) {
+ $addr = "$4!" . ($1? "$1" : "") . $3 . "\@$2";
+ }
+ $addr =~ s/(@.+)/\L$1/o unless($opts{'i'});
+ $addr = lc($addr) if($opts{'i'});
+ (my $domAddr = $addr) =~ s/^[^@]+\@//o; # get domain only
+ if($status eq 'sent') {
+
+ # was it actually forwarded, rather than delivered?
+ if($toRmdr =~ /forwarded as /o) {
+ ++$msgsFwdd;
+ next;
+ }
+ ++$recipDomCnt unless(${$recipDom{$domAddr}}[$msgCntI]);
+ ++${$recipDom{$domAddr}}[$msgCntI];
+ ${$recipDom{$domAddr}}[$msgDlyAvgI] += $delay;
+ if(! ${$recipDom{$domAddr}}[$msgDlyMaxI] ||
+ $delay > ${$recipDom{$domAddr}}[$msgDlyMaxI])
+ {
+ ${$recipDom{$domAddr}}[$msgDlyMaxI] = $delay
+ }
+ ++$recipUserCnt unless(${$recipUser{$addr}}[$msgCntI]);
+ ++${$recipUser{$addr}}[$msgCntI];
+ ++$dlvPerHr[$msgHr];
+ ++${$msgsPerDay{$revMsgDateStr}}[1];
+ ++$msgsDlvrd;
+ if($msgSizes{$qid}) {
+ ${$recipDom{$domAddr}}[$msgSizeI] += $msgSizes{$qid};
+ ${$recipUser{$addr}}[$msgSizeI] += $msgSizes{$qid};
+ $sizeDlvrd += $msgSizes{$qid};
+ } else {
+ ${$recipDom{$domAddr}}[$msgSizeI] += 0;
+ ${$recipUser{$addr}}[$msgSizeI] += 0;
+ $noMsgSize{$qid} = $addr;
+ push(@{$msgDetail{$qid}}, "(sender not in log)") if($opts{'e'});
+ # put this back later? mebbe with -v?
+ # msg_warn("no message size for qid: $qid");
+ }
+ push(@{$msgDetail{$qid}}, $addr) if($opts{'e'});
+ } elsif($status eq 'deferred') {
+ my ($deferredReas) = /, status=deferred \(([^\)]+)/o;
+ unless(defined($opts{'verbMsgDetail'})) {
+ $deferredReas = said_string_trimmer($deferredReas, 65);
+ $deferredReas =~ s/^[0-9]{3} //o;
+ $deferredReas =~ s/^connect to //o;
+ }
+ ++$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}}[$msgDlyMaxI] = $delay
+ }
+ } elsif($status eq 'bounced') {
+ my ($bounceReas) = /, status=bounced \((.+)\)/o;
+ unless(defined($opts{'verbMsgDetail'})) {
+ $bounceReas = said_string_trimmer($bounceReas, 66);
+ $bounceReas =~ s/^[0-9]{3} //o;
+ }
+ ++$bounced{$relay}{$bounceReas};
+ ++$bncPerHr[$msgHr];
+ ++${$msgsPerDay{$revMsgDateStr}}[3];
+ ++$msgsBncd;
+ } else {
+# print UNPROCD "$_\n";
+ }
+ }
+ elsif($cmd eq 'pickup' && /: (sender|uid)=/o) {
+ #
+ # Warning: this code in two places!
+ #
+ ++$rcvPerHr[$msgHr];
+ ++${$msgsPerDay{$revMsgDateStr}}[0];
+ ++$msgsRcvd;
+ ++$rcvdMsg{$qid}; # quick-set a flag
+ }
+ elsif($cmd eq 'smtp') {
+ if(/.* connect to ([^:]+): ([^;]+); address [0-9\.]+ port.*$/o) {
+ ++$smtpMsgs{lc($2)}{$1};
+ } elsif(/.* connect to ([^[]+)\[[0-9\.]+]: (.+) \(port \d+\)$/o) {
+ ++$smtpMsgs{lc($2)}{$1};
+ } else {
+# print UNPROCD "$_\n";
+ }
+ }
+ else
+ {
+# print UNPROCD "$_\n";
+ }
+ }
+}
+
+# debugging
+#close(UNPROCD) ||
+# die "problem closing \"unprocessed\": $!\n";
+
+if(defined($dateStr)) {
+ print "Postfix log summaries for $dateStr\n";
+}
+
+print "\nGrand Totals\n------------\n";
+print "messages\n\n";
+printf " %6d%s received\n", adj_int_units($msgsRcvd);
+printf " %6d%s delivered\n", adj_int_units($msgsDlvrd);
+printf " %6d%s forwarded\n", adj_int_units($msgsFwdd);
+printf " %6d%s deferred", adj_int_units($msgsDfrd);
+printf " (%d%s deferrals)", adj_int_units($msgsDfrdCnt) if($msgsDfrdCnt);
+print "\n";
+printf " %6d%s bounced\n", adj_int_units($msgsBncd);
+printf " %6d%s rejected\n", adj_int_units($msgsRjctd);
+print "\n";
+printf " %6d%s bytes received\n", adj_int_units($sizeRcvd);
+printf " %6d%s bytes delivered\n", adj_int_units($sizeDlvrd);
+printf " %6d%s senders\n", adj_int_units($sendgUserCnt);
+printf " %6d%s sending hosts/domains\n", adj_int_units($sendgDomCnt);
+printf " %6d%s recipients\n", adj_int_units($recipUserCnt);
+printf " %6d%s recipient hosts/domains\n", adj_int_units($recipDomCnt);
+
+# ---Begin: SMTPD_STATS_SUPPORT---
+if(defined($opts{'smtpdStats'})) {
+ print "\nsmtpd\n\n";
+ printf " %6d%s connections\n", adj_int_units($smtpdConnCnt);
+ printf " %6d%s hosts/domains\n", adj_int_units(int(keys %smtpdPerDom));
+ printf " %6d avg. connect time (seconds)\n",
+ $smtpdConnCnt > 0? ($smtpdTotTime / $smtpdConnCnt) + .5 : 0;
+ {
+ my ($sec, $min, $hr) = get_smh($smtpdTotTime);
+ printf " %2d:%02d:%02d total connect time\n",
+ $hr, $min, $sec;
+ }
+}
+# ---End: SMTPD_STATS_SUPPORT---
+
+print "\n";
+
+print_problems_reports() if(defined($opts{'pf'}));
+
+print_per_day_summary(\%msgsPerDay) if($dayCnt > 1);
+print_per_hour_summary(\@rcvPerHr, \@dlvPerHr, \@dfrPerHr, \@bncPerHr,
+ \@rejPerHr, $dayCnt);
+
+print_recip_domain_summary(\%recipDom, $opts{'h'});
+print_sending_domain_summary(\%sendgDom, $opts{'h'});
+
+# ---Begin: SMTPD_STATS_SUPPORT---
+if(defined($opts{'smtpdStats'})) {
+ print_per_day_smtpd(\%smtpdPerDay, $dayCnt) if($dayCnt > 1);
+ print_per_hour_smtpd(\@smtpdPerHr, $dayCnt);
+ print_domain_smtpd_summary(\%smtpdPerDom, $opts{'h'});
+}
+# ---End: SMTPD_STATS_SUPPORT---
+
+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_hash_by_key(\%noMsgSize, "Messages with no size data", 0, 1);
+
+print_problems_reports() unless(defined($opts{'pf'}));
+
+print_detailed_msg_data(\%msgDetail, "Message detail", $opts{'q'}) if($opts{'e'});
+
+# Print "problems" reports
+sub print_problems_reports {
+ unless($opts{'noDeferralDetail'}) {
+ print_nested_hash(\%deferred, "message deferral detail", $opts{'q'});
+ }
+ unless($opts{'noBounceDetail'}) {
+ print_nested_hash(\%bounced, "message bounce detail (by relay)", $opts{'q'});
+ }
+ unless($opts{'noRejectDetail'}) {
+ print_nested_hash(\%rejects, "message reject detail", $opts{'q'});
+ }
+ print_nested_hash(\%smtpMsgs, "smtp delivery failures", $opts{'q'});
+ print_nested_hash(\%warnings, "Warnings", $opts{'q'});
+ print_nested_hash(\%fatals, "Fatal Errors", 0, $opts{'q'});
+ print_nested_hash(\%panics, "Panics", 0, $opts{'q'});
+ print_hash_by_cnt_vals(\%masterMsgs,"Master daemon messages", 0, $opts{'q'});
+}
+
+if($opts{'mailq'}) {
+ # flush stdout first cuz of asynchronousity
+ $| = 1;
+ print "\nCurrent Mail Queue\n------------------\n";
+ system($mailqCmd);
+}
+
+# print "per-day" traffic summary
+# (done in a subroutine only to keep main-line code clean)
+sub print_per_day_summary {
+ my($msgsPerDay) = @_;
+ my $value;
+ print <<End_Of_Per_Day_Heading;
+
+Per-Day Traffic Summary
+ date received delivered deferred bounced rejected
+ --------------------------------------------------------------------
+End_Of_Per_Day_Heading
+
+ foreach (sort { $a <=> $b } keys(%$msgsPerDay)) {
+ my ($msgYr, $msgMon, $msgDay) = unpack("A4 A2 A2", $_);
+ if($isoDateTime) {
+ printf " %04d-%02d-%02d ", $msgYr, $msgMon + 1, $msgDay
+ } else {
+ my $msgMonStr = $monthNames[$msgMon];
+ printf " $msgMonStr %2d $msgYr", $msgDay;
+ }
+ foreach $value (@{$msgsPerDay->{$_}}) {
+ my $value2 = $value? $value : 0;
+ printf " %6d%s", adj_int_units($value2);
+ }
+ print "\n";
+ }
+}
+
+# print "per-hour" traffic summary
+# (done in a subroutine only to keep main-line code clean)
+sub print_per_hour_summary {
+ my ($rcvPerHr, $dlvPerHr, $dfrPerHr, $bncPerHr, $rejPerHr, $dayCnt) = @_;
+ my $reportType = $dayCnt > 1? 'Daily Average' : 'Summary';
+ my ($hour, $value);
+ print <<End_Of_Per_Hour_Heading;
+
+Per-Hour Traffic $reportType
+ time received delivered deferred bounced rejected
+ --------------------------------------------------------------------
+End_Of_Per_Hour_Heading
+
+ for($hour = 0; $hour < 24; ++$hour) {
+ if($isoDateTime) {
+ printf " %02d:00-%02d:00", $hour, $hour + 1;
+ } else {
+ printf " %02d00-%02d00 ", $hour, $hour + 1;
+ }
+ foreach $value (@$rcvPerHr[$hour], @$dlvPerHr[$hour],
+ @$dfrPerHr[$hour], @$bncPerHr[$hour],
+ @$rejPerHr[$hour])
+ {
+ my $units = ' ';
+ $value = ($value / $dayCnt) + 0.5 if($dayCnt);
+ printf " %6d%s", adj_int_units($value);
+ }
+ print "\n";
+ }
+}
+
+# print "per-recipient-domain" traffic summary
+# (done in a subroutine only to keep main-line code clean)
+sub print_recip_domain_summary {
+ use vars '$hashRef';
+ local($hashRef) = $_[0];
+ my($cnt) = $_[1];
+ return if($cnt == 0);
+ my $topCnt = $cnt > 0? "(top $cnt)" : "";
+ my $avgDly;
+ print <<End_Of_Recip_Domain_Heading;
+
+Host/Domain Summary: Message Delivery $topCnt
+ sent 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]);
+ } 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_time_units($avgDly),
+ adj_time_units(${$hashRef->{$_}}[$msgDlyMaxI]),
+ $_;
+ last if --$cnt == 0;
+ }
+}
+
+# print "per-sender-domain" traffic summary
+# (done in a subroutine only to keep main-line code clean)
+sub print_sending_domain_summary {
+ use vars '$hashRef';
+ local($hashRef) = $_[0];
+ my($cnt) = $_[1];
+ return if($cnt == 0);
+ my $topCnt = $cnt > 0? "(top $cnt)" : "";
+ print <<End_Of_Sender_Domain_Heading;
+
+Host/Domain Summary: Messages Received $topCnt
+ msg cnt bytes host/domain
+ -------- ------- -----------
+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]),
+ $_;
+ last if --$cnt == 0;
+ }
+}
+
+# print "per-user" data sorted in descending order
+# order (i.e.: highest first)
+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";
+ foreach (reverse sort { ${$hashRef->{$a}}[$index] <=>
+ ${$hashRef->{$b}}[$index] }
+ keys(%$hashRef))
+ {
+ printf " %6d%s %s\n", adj_int_units(${$hashRef->{$_}}[$index]), $_;
+ last if --$cnt == 0;
+ }
+}
+
+# ---Begin: SMTPD_STATS_SUPPORT---
+
+# print "per-hour" smtpd connection summary
+# (done in a subroutine only to keep main-line code clean)
+sub print_per_hour_smtpd {
+ my ($smtpdPerHr, $dayCnt) = @_;
+ my ($hour, $value);
+ if($dayCnt > 1) {
+ print <<End_Of_Per_Hour_Smtp_Average;
+
+Per-Hour SMTPD Connection Daily Average
+ hour connections time conn.
+ -------------------------------------
+End_Of_Per_Hour_Smtp_Average
+ } else {
+ print <<End_Of_Per_Hour_Smtp;
+
+Per-Hour SMTPD Connection Summary
+ hour connections time conn. avg./conn. max. time
+ --------------------------------------------------------------------
+End_Of_Per_Hour_Smtp
+ }
+
+ for($hour = 0; $hour < 24; ++$hour) {
+ $smtpdPerHr[$hour]->[0] || next;
+ my $avg = int($smtpdPerHr[$hour]->[0]?
+ ($smtpdPerHr[$hour]->[1]/$smtpdPerHr[$hour]->[0]) + .5 : 0);
+ if($dayCnt > 1) {
+ $smtpdPerHr[$hour]->[0] /= $dayCnt;
+ $smtpdPerHr[$hour]->[1] /= $dayCnt;
+ $smtpdPerHr[$hour]->[0] += .5;
+ $smtpdPerHr[$hour]->[1] += .5;
+ }
+ my($sec, $min, $hr) = get_smh($smtpdPerHr[$hour]->[1]);
+
+ if($isoDateTime) {
+ printf " %02d:00-%02d:00", $hour, $hour + 1;
+ } else {
+ printf " %02d00-%02d00 ", $hour, $hour + 1;
+ }
+ printf " %6d%s %2d:%02d:%02d",
+ adj_int_units($smtpdPerHr[$hour]->[0]),
+ $hr, $min, $sec;
+ if($dayCnt < 2) {
+ printf " %6ds %6ds",
+ $avg,
+ $smtpdPerHr[$hour]->[2];
+ }
+ print "\n";
+ }
+}
+
+
+# print "per-day" smtpd connection summary
+# (done in a subroutine only to keep main-line code clean)
+sub print_per_day_smtpd {
+ my ($smtpdPerDay, $dayCnt) = @_;
+ print <<End_Of_Per_Day_Smtp;
+
+Per-Day SMTPD Connection Summary
+ date connections time conn. avg./conn. max. time
+ --------------------------------------------------------------------
+End_Of_Per_Day_Smtp
+
+ foreach (sort { $a <=> $b } keys(%$smtpdPerDay)) {
+ my ($msgYr, $msgMon, $msgDay) = unpack("A4 A2 A2", $_);
+ if($isoDateTime) {
+ printf " %04d-%02d-%02d ", $msgYr, $msgMon + 1, $msgDay
+ } else {
+ my $msgMonStr = $monthNames[$msgMon];
+ printf " $msgMonStr %2d $msgYr", $msgDay;
+ }
+
+ my $avg = (${$smtpdPerDay{$_}}[1]/${$smtpdPerDay{$_}}[0]) + .5;
+ my($sec, $min, $hr) = get_smh(${$smtpdPerDay{$_}}[1]);
+
+ printf " %6d%s %2d:%02d:%02d %6ds %6ds\n",
+ adj_int_units(${$smtpdPerDay{$_}}[0]),
+ $hr, $min, $sec,
+ $avg,
+ ${$smtpdPerDay{$_}}[2];
+ }
+}
+
+# print "per-domain-smtpd" connection summary
+# (done in a subroutine only to keep main-line code clean)
+sub print_domain_smtpd_summary {
+ use vars '$hashRef';
+ local($hashRef) = $_[0];
+ my($cnt) = $_[1];
+ return if($cnt == 0);
+ my $topCnt = $cnt > 0? "(top $cnt)" : "";
+ my $avgDly;
+ print <<End_Of_Domain_Smtp_Heading;
+
+Host/Domain Summary: SMTPD Connections $topCnt
+ connections time conn. avg./conn. max. time host/domain
+ ----------- ---------- ---------- --------- -----------
+End_Of_Domain_Smtp_Heading
+
+ foreach (reverse sort by_count_then_size keys(%$hashRef)) {
+ my $avg = (${$hashRef->{$_}}[1]/${$hashRef->{$_}}[0]) + .5;
+ my ($sec, $min, $hr) = get_smh(${$hashRef->{$_}}[1]);
+
+ printf " %6d%s %2d:%02d:%02d %6ds %6ds %s\n",
+ adj_int_units(${$hashRef->{$_}}[0]),
+ $hr, $min, $sec,
+ $avg,
+ ${$hashRef->{$_}}[2],
+ $_;
+ last if --$cnt == 0;
+ }
+}
+
+# ---End: SMTPD_STATS_SUPPORT---
+
+# print hash contents sorted by numeric values in descending
+# order (i.e.: highest first)
+sub print_hash_by_cnt_vals {
+ my($hashRef, $title, $cnt, $quiet) = @_;
+ my $dottedLine;
+ $title = sprintf "%s%s", $cnt? "top $cnt " : "", $title;
+ unless(%$hashRef) {
+ return if($quiet);
+ $dottedLine = ": none";
+ } else {
+ $dottedLine = "\n" . "-" x length($title);
+ }
+ printf "\n$title$dottedLine\n";
+ really_print_hash_by_cnt_vals($hashRef, $cnt, ' ');
+}
+
+# print hash contents sorted by key in ascending order
+sub print_hash_by_key {
+ my($hashRef, $title, $cnt, $quiet) = @_;
+ my $dottedLine;
+ $title = sprintf "%s%s", $cnt? "first $cnt " : "", $title;
+ unless(%$hashRef) {
+ return if($quiet);
+ $dottedLine = ": none";
+ } else {
+ $dottedLine = "\n" . "-" x length($title);
+ }
+ printf "\n$title$dottedLine\n";
+ foreach (sort keys(%$hashRef))
+ {
+ printf " %s %s\n", $_, $hashRef->{$_};
+ last if --$cnt == 0;
+ }
+}
+
+# print "nested" hashes
+sub print_nested_hash {
+ my($hashRef, $title, $quiet) = @_;
+ my $dottedLine;
+ unless(%$hashRef) {
+ return if($quiet);
+ $dottedLine = ": none";
+ } else {
+ $dottedLine = "\n" . "-" x length($title);
+ }
+ printf "\n$title$dottedLine\n";
+ walk_nested_hash($hashRef, 0);
+}
+
+# "walk" a "nested" hash
+sub walk_nested_hash {
+ my ($hashRef, $level) = @_;
+ $level += 2;
+ my $indents = ' ' x $level;
+ my ($keyName, $hashVal) = each(%$hashRef);
+
+ if(ref($hashVal) eq 'HASH') {
+ foreach (sort keys %$hashRef) {
+ print "$indents$_\n";
+ walk_nested_hash($hashRef->{$_}, $level);
+ }
+ } else {
+ really_print_hash_by_cnt_vals($hashRef, 0, $indents);
+# print "\n"
+ }
+}
+
+# print per-message info in excruciating detail :-)
+sub print_detailed_msg_data {
+ use vars '$hashRef';
+ local($hashRef) = $_[0];
+ my($title, $quiet) = @_[1,2];
+ my $dottedLine;
+ unless(%$hashRef) {
+ return if($quiet);
+ $dottedLine = ": none";
+ } else {
+ $dottedLine = "\n" . "-" x length($title);
+ }
+ printf "\n$title$dottedLine\n";
+ foreach (sort by_domain_then_user keys(%$hashRef))
+ {
+ printf " %s %s\n", $_, shift(@{$hashRef->{$_}});
+ foreach (@{$hashRef->{$_}}) {
+ print " $_\n";
+ }
+ print "\n";
+ }
+}
+
+# *really* print hash contents sorted by numeric values in descending
+# order (i.e.: highest first) :-)
+sub really_print_hash_by_cnt_vals {
+ my($hashRef, $cnt, $indents) = @_;
+
+ foreach (reverse sort { $hashRef->{$a} <=> $hashRef->{$b} }
+ keys(%$hashRef))
+ {
+ printf "$indents%6d%s %s\n", adj_int_units($hashRef->{$_}), $_;
+ last if --$cnt == 0;
+ }
+}
+
+# subroutine to sort by domain, then user in domain, then by queue i.d.
+# Note: mixing Internet-style domain names and UUCP-style bang-paths
+# may confuse this thing. An attempt is made to use the first host
+# preceding the username in the bang-path as the "domain" if none is
+# found otherwise.
+sub by_domain_then_user {
+ # first see if we can get "user@somedomain"
+ my($userNameA, $domainA) = split(/\@/, ${$hashRef->{$a}}[0]);
+ my($userNameB, $domainB) = split(/\@/, ${$hashRef->{$b}}[0]);
+
+ # try "somedomain!user"?
+ ($userNameA, $domainA) = (split(/!/, ${$hashRef->{$a}}[0]))[-1,-2]
+ unless($domainA);
+ ($userNameB, $domainB) = (split(/!/, ${$hashRef->{$b}}[0]))[-1,-2]
+ unless($domainB);
+
+ # now re-order "mach.host.dom"/"mach.host.do.co" to
+ # "host.dom.mach"/"host.do.co.mach"
+ $domainA =~ s/^(.*)\.([^\.]+)\.([^\.]{3}|[^\.]{2,3}\.[^\.]{2})$/$2.$3.$1/o
+ if($domainA);
+ $domainB =~ s/^(.*)\.([^\.]+)\.([^\.]{3}|[^\.]{2,3}\.[^\.]{2})$/$2.$3.$1/o
+ if($domainB);
+
+ # oddly enough, doing this here is marginally faster than doing
+ # an "if-else", above. go figure.
+ $domainA = "" unless($domainA);
+ $domainB = "" unless($domainB);
+
+ if($domainA lt $domainB) {
+ return -1;
+ } elsif($domainA gt $domainB) {
+ return 1;
+ } else {
+ # disregard leading bang-path
+ $userNameA =~ s/^.*!//o;
+ $userNameB =~ s/^.*!//o;
+ if($userNameA lt $userNameB) {
+ return -1;
+ } elsif($userNameA gt $userNameB) {
+ return 1;
+ } else {
+ if($a lt $b) {
+ return -1;
+ } elsif($a gt $b) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+# Subroutine used by host/domain reports to sort by count, then size.
+# We "fix" un-initialized values here as well. Very ugly and un-
+# 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]);
+ } else {
+ return(${$hashRef->{$a}}[$msgCntI] <=>
+ ${$hashRef->{$b}}[$msgCntI]);
+ }
+}
+
+# return a date string to match in log
+sub get_datestr {
+ my $dateOpt = $_[0];
+
+ my $aDay = 60 * 60 * 24;
+
+ my $time = time();
+ if($dateOpt eq "yesterday") {
+ $time -= $aDay;
+ } elsif($dateOpt ne "today") {
+ die "$usageMsg\n";
+ }
+ my ($t_mday, $t_mon) = (localtime($time))[3,4];
+
+ return sprintf("%s %2d", $monthNames[$t_mon], $t_mday);
+}
+
+# if there's a real domain: uses that. Otherwise uses the first
+# three octets of the IP addr. (In the latter case: usually pretty
+# safe to assume it's a dialup with a class C IP addr.) Lower-
+# cases returned domain name.
+sub gimme_domain {
+ $_ = $_[0];
+ my($domain, $ipAddr);
+
+ # split domain/ipaddr into separates
+ unless((($domain, $ipAddr) = /^([^\[]+)\[([^\]]+)\]:?\s*$/o) == 2) {
+ # more exhaustive method
+ ($domain, $ipAddr) = /^([^\[\(]+)[\[\(]([^\]\)]+)[\]\)]:?\s*$/o;
+ }
+
+# print STDERR "dbg: in=\"$_\", domain=\"$domain\", ipAddr=\"$ipAddr\"\n";
+ # now re-order "mach.host.dom"/"mach.host.do.co" to
+ # "host.dom.mach"/"host.do.co.mach"
+ if($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/\.[0-9]+$//o;
+ } else {
+ $domain =~
+ s/^(.*)\.([^\.]+)\.([^\.]{3}|[^\.]{2,3}\.[^\.]{2})$/\L$2.$3/o;
+ }
+
+ return $domain;
+}
+
+# Return (value, units) for integer
+sub adj_int_units {
+ my $value = $_[0];
+ my $units = ' ';
+ $value = 0 unless($value);
+ if($value > $divByOneMegAt) {
+ $value /= $oneMeg;
+ $units = 'm'
+ } elsif($value > $divByOneKAt) {
+ $value /= $oneK;
+ $units = 'k'
+ }
+ return($value, $units);
+}
+
+# Return (value, units) for time
+sub adj_time_units {
+ my $value = $_[0];
+ my $units = 's';
+ $value = 0 unless($value);
+ if($value > 3600) {
+ $value /= 3600;
+ $units = 'h'
+ } elsif($value > 60) {
+ $value /= 60;
+ $units = 'm'
+ }
+ return($value, $units);
+}
+
+# Trim a "said:" string, if necessary. Add elipses to show it.
+sub said_string_trimmer {
+ my($trimmedString, $maxLen) = @_;
+
+ while(length($trimmedString) > $maxLen) {
+ if($trimmedString =~ /^.* said: /o) {
+ $trimmedString =~ s/^.* said: //o;
+ } elsif($trimmedString =~ /^.*: */o) {
+ $trimmedString =~ s/^.*?: *//o;
+ } else {
+ $trimmedString = substr($trimmedString, 0, $maxLen - 3) . "...";
+ last;
+ }
+ }
+
+ return $trimmedString;
+}
+
+# Trim a string, if necessary. Add elipses to show it.
+sub string_trimmer {
+ my($trimmedString, $maxLen, $doNotTrim) = @_;
+
+ $trimmedString = substr($trimmedString, 0, $maxLen - 3) . "..."
+ if(! $doNotTrim && (length($trimmedString) > $maxLen));
+ return $trimmedString;
+}
+
+# Get seconds, minutes and hours from seconds
+sub get_smh {
+ my $sec = shift @_;
+ my $hr = int($sec / 3600);
+ $sec -= $hr * 3600;
+ my $min = int($sec / 60);
+ $sec -= $min * 60;
+ return($sec, $min, $hr);
+}
+
+###
+### Warning and Error Routines
+###
+
+# Emit warning message to stderr
+sub msg_warn {
+ warn "warning: $progName: $_[0]\n";
+}
+
diff --git a/postfix/postfix-db4.patch b/postfix/postfix-db4.patch
new file mode 100644
index 0000000..583fa1e
--- /dev/null
+++ b/postfix/postfix-db4.patch
@@ -0,0 +1,16 @@
+--- postfix-1.1.11.orig/src/util/dict_db.c Mon Oct 14 18:54:26 2002
++++ postfix-1.1.11/src/util/dict_db.c Mon Oct 14 19:52:16 2002
+@@ -520,8 +520,13 @@
+ msg_fatal("set DB cache size %d: %m", DICT_DB_CACHE_SIZE);
+ if (type == DB_HASH && db->set_h_nelem(db, DICT_DB_NELM) != 0)
+ msg_fatal("set DB hash element count %d: %m", DICT_DB_NELM);
++#if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
++ if ((errno = db->open(db, 0, db_path, 0, type, db_flags, 0644)) != 0)
++ msg_fatal("open database %s: %m", db_path);
++#else
+ if ((errno = db->open(db, db_path, 0, type, db_flags, 0644)) != 0)
+ msg_fatal("open database %s: %m", db_path);
++#endif
+ if ((errno = db->fd(db, &dbfd)) != 0)
+ msg_fatal("get database file descriptor: %m");
+ #endif
diff --git a/postfix/postfix-sasl2.patch b/postfix/postfix-sasl2.patch
new file mode 100644
index 0000000..184cd2b
--- /dev/null
+++ b/postfix/postfix-sasl2.patch
@@ -0,0 +1,715 @@
+diff -ru postfix-1.1.4-orig/README_FILES/SASL_README postfix-1.1.4/README_FILES/SASL_README
+--- postfix-1.1.4-orig/README_FILES/SASL_README Sat May 26 11:32:47 2001
++++ postfix-1.1.4/README_FILES/SASL_README Mon Mar 11 14:04:58 2002
+@@ -26,6 +26,14 @@
+ Note that this seems to be related to the auto_transition switch in
+ SASL. Note also that the Cyrus SASL documentation says that it is
+ pointless to enable that if you use "sasldb" for "pwcheck_method".
++Later versions of the SASL 1.5.x series should also work.
++
++Postfix+SASL 2.1.1 appears to work on Mandrake Linux 8.1 (pwcheck_method
++set to saslauthd or auxprop). Note that the 'auxprop' pwcheck_method
++replaces the 'sasldb' method from SASL 1.5.x. Postfix may need
++write access to /etc/sasldb2 if you are using the auto_transition
++feature, or if you are using an authentication mechanism such as OTP
++that needs to update secrets in the database.
+
+ Introduction
+ ============
+@@ -50,20 +58,22 @@
+ Building the SASL library
+ =========================
+
+-Postfix appears to work with cyrus-sasl-1.5.5, which is available
+-from:
++Postfix appears to work with cyrus-sasl-1.5.5 or cyrus-sasl-2.1.1,
++which are available from:
+
+ ftp://ftp.andrew.cmu.edu/pub/cyrus-mail/
+
+ IMPORTANT: if you install the Cyrus SASL libraries as per the default,
+-you will have to symlink /usr/lib/sasl -> /usr/local/lib/sasl.
++you will have to symlink /usr/lib/sasl -> /usr/local/lib/sasl for
++version 1.5.5 or /usr/lib/sasl2 -> /usr/local/lib/sasl2 for version 2.1.1.
+
+ Reportedly, Microsoft Internet Explorer version 5 requires the
+ non-standard SASL LOGIN authentication method. To enable this
+ authentication method, specify ``./configure --enable-login''.
+
+ If you install the Cyrus SASL libraries as per the default, you
+-will have to symlink /usr/lib/sasl -> /usr/local/lib/sasl.
++will have to symlink /usr/lib/sasl -> /usr/local/lib/sasl for version 1.5.5
++or symlink /usr/lib/sasl2 -> /usr/local/lib/sasl2 for version 2.1.1.
+
+ Building Postfix with SASL authentication support
+ =================================================
+@@ -72,19 +82,33 @@
+ assumes that the Cyrus SASL include files are in /usr/local/include,
+ and that the Cyrus SASL libraries are in /usr/local/lib.
+
+-On some systems this generates the necessary Makefile definitions:
++On some systems this generates the necessary Makefile definitions for
++Cyrus SASL 1.5.5:
+
+ % make tidy # if you have left-over files from a previous build
+ % make makefiles CCARGS="-DUSE_SASL_AUTH -I/usr/local/include" \
+ AUXLIBS="-L/usr/local/lib -lsasl"
+
++On some systems this generates the necessary Makefile definitions for
++Cyrus SASL 2.1.1:
++
++ % make tidy # if you have left-over files from a previous build
++ % make makefiles CCARGS="-DUSE_SASL_AUTH -I/usr/local/include/sasl" \
++ AUXLIBS="-L/usr/local/lib -lsasl2"
++
+ On Solaris 2.x you need to specify run-time link information,
+ otherwise ld.so will not find the SASL shared library:
+
++(for version 1.5.5):
+ % make tidy # if you have left-over files from a previous build
+ % make makefiles CCARGS="-DUSE_SASL_AUTH -I/usr/local/include" \
+ AUXLIBS="-L/usr/local/lib -R/usr/local/lib -lsasl"
+
++(for version 2.1.1):
++ % make tidy # if you have left-over files from a previous build
++ % make makefiles CCARGS="-DUSE_SASL_AUTH -I/usr/local/include/sasl" \
++ AUXLIBS="-L/usr/local/lib -R/usr/local/lib -lsasl2"
++
+ Enabling SASL authentication in the Postfix SMTP server
+ =======================================================
+
+@@ -101,23 +125,41 @@
+ smtpd_recipient_restrictions =
+ permit_mynetworks permit_sasl_authenticated ...
+
+-In /usr/local/lib/sasl/smtpd.conf you need to specify how the server
+-should validate client passwords.
++In /usr/local/lib/sasl/smtpd.conf (for version 1.5.5) or
++/usr/local/lib/sasl2/smtpd.conf (for version 2.1.1) you need to
++specify how the server should validate client passwords.
+
+ In order to authenticate against the UNIX password database, try:
+
+ /usr/local/lib/sasl/smtpd.conf:
+ pwcheck_method: pwcheck
++ (use /usr/local/lib/sasl2/smtpd.conf with version 2.1.1)
+
+ The pwcheck daemon is contained in the cyrus-sasl source tarball.
+
+-In order to authenticate against SASL's own password database:
++Alternately, in SASL 1.5.27 and later (including 2.1.1), try:
++
++ /usr/local/lib/sasl/smtpd.conf:
++ pwcheck_method: saslauthd
++ (use /usr/local/lib/sasl2/smtpd.conf with version 2.1.1)
++
++The saslauthd daemon is also contained in the cyrus-sasl source tarball.
++It is more flexible than the pwcheck daemon, in that it can authenticate
++against PAM and various other sources.
++
++In order to authenticate against SASL's own password database in version 1.5.5:
+
+ /usr/local/lib/sasl/smtpd.conf:
+ pwcheck_method: sasldb
+
+-This will use the SASL password file (default: /etc/sasldb), which
+-is maintained with the saslpasswd command (part of the Cyrus SASL
++or in version 2.1.1:
++
++ /usr/local/lib/sasl2/smtpd.conf:
++ pwcheck_method: auxprop
++
++This will use the SASL password file (default: /etc/sasldb in
++version 1.5.5, or /etc/sasldb2 in version 2.1.1), which is maintained
++with the saslpasswd or saslpasswd2 command (part of the Cyrus SASL
+ software). On some poorly-supported systems the saslpasswd command
+ needs to be run multiple times before it stops complaining. The
+ Postfix SMTP server needs read access to the sasldb file - you may
+diff -ru postfix-1.1.4-orig/conf/sample-auth.cf postfix-1.1.4/conf/sample-auth.cf
+--- postfix-1.1.4-orig/conf/sample-auth.cf Mon Mar 11 00:44:43 2002
++++ postfix-1.1.4/conf/sample-auth.cf Mon Mar 11 17:36:22 2002
+@@ -23,6 +23,7 @@
+ #
+ # In order to enable server-side authentication, build Postfix with
+ # SASL support, and install a configuration file /usr/lib/sasl/smtpd.conf
++# (for SASL version 1) or /usr/lib/sasl2/smtpd.conf (for SASL version 2)
+ # with as contents, for example,
+ #
+ # pwcheck_method: sasldb
+@@ -51,6 +52,10 @@
+ # nodictionary: disallow methods subject to passive (dictionary) attack
+ # noanonymous: disallow methods that allow anonymous authentication
+ #
++# An additional options is available in SASL version 2:
++#
++# mutual_auth: only allow methods that provide mutual authentication
++#
+ # By default, the Postfix SMTP server accepts plaintext passwords but
+ # not anonymous logins.
+ #
+@@ -104,6 +109,10 @@
+ # nodictionary: disallow methods subject to passive (dictionary) attack
+ # noanonymous: disallow methods that allow anonymous authentication
+ #
++# An additional options is available in SASL version 2:
++#
++# mutual_auth: only allow methods that provide mutual authentication
++#
+ # By default, the Postfix SMTP client will not use plaintext passwords.
+ #
+ #smtp_sasl_security_options =
+diff -ru postfix-1.1.4-orig/src/lmtp/lmtp_sasl_glue.c postfix-1.1.4/src/lmtp/lmtp_sasl_glue.c
+--- postfix-1.1.4-orig/src/lmtp/lmtp_sasl_glue.c Fri Jan 19 15:46:44 2001
++++ postfix-1.1.4/src/lmtp/lmtp_sasl_glue.c Mon Mar 11 17:35:33 2002
+@@ -116,6 +116,9 @@
+ "noactive", SASL_SEC_NOACTIVE,
+ "nodictionary", SASL_SEC_NODICTIONARY,
+ "noanonymous", SASL_SEC_NOANONYMOUS,
++#if SASL_VERSION_MAJOR >= 2
++ "mutual_auth", SASL_SEC_MUTUAL_AUTH,
++#endif
+ 0,
+ };
+
+@@ -127,6 +130,44 @@
+ #define STR(x) vstring_str(x)
+
+ /*
++ * Macros to handle API differences between SASLv1 and SASLv2.
++ * Specifics:
++ * The SASL_LOG_* constants were renamed in SASLv2.
++ * SASLv2's sasl_client_new takes two new parameters to specify local
++ * and remote IP addresses for auth mechs that use them.
++ * SASLv2's sasl_client_start function no longer takes the secret parameter.
++ * SASLv2's sasl_decode64 function takes an extra parameter for the
++ * length of the output buffer.
++ *
++ * The other major change is that SASLv2 now takes more responsibility for
++ * deallocating memory that it allocates internally. Thus, some of the
++ * function parameters are now 'const', to make sure we don't try to free
++ * them too. This is dealt with in the code later on.
++ */
++
++#if SASL_VERSION_MAJOR < 2
++/* SASL version 1.x */
++#define SASL_LOG_WARN SASL_LOG_WARNING
++#define SASL_LOG_NOTE SASL_LOG_INFO
++#define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
++ sasl_client_new(srv, fqdn, prompt, secflags, pconn)
++#define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
++ sasl_client_start(conn, mechlst, secret, prompt, clout, cllen, mech)
++#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
++ sasl_decode64(in, inlen, out, outlen)
++#endif
++
++#if SASL_VERSION_MAJOR >= 2
++/* SASL version > 2.x */
++#define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
++ sasl_client_new(srv, fqdn, lport, rport, prompt, secflags, pconn)
++#define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
++ sasl_client_start(conn, mechlst, prompt, clout, cllen, mech)
++#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
++ sasl_decode64(in, inlen, out, outmaxlen, outlen)
++#endif
++
++ /*
+ * Per-host login/password information.
+ */
+ static MAPS *lmtp_sasl_passwd_map;
+@@ -137,14 +178,18 @@
+ const char *message)
+ {
+ switch (priority) {
+- case SASL_LOG_ERR:
+- case SASL_LOG_WARNING:
+- msg_warn("%s", message);
++ case SASL_LOG_ERR: /* unusual errors */
++ case SASL_LOG_WARN: /* non-fatal warnings */
++ msg_warn("SASL authentication problem: %s", message);
+ break;
+- case SASL_LOG_INFO:
++ case SASL_LOG_NOTE: /* other info */
+ if (msg_verbose)
+- msg_info("%s", message);
++ msg_info("SASL authentication info: %s", message);
+ break;
++#if SASL_VERSION_MAJOR >= 2
++ case SASL_LOG_FAIL: /* authentication failures - SASLv2 only */
++ msg_warn("SASL authentication failure: %s", message);
++#endif
+ }
+ return (SASL_OK);
+ }
+@@ -317,7 +362,9 @@
+ memcpy((char *) state->sasl_callbacks, callbacks, sizeof(callbacks));
+ for (cp = state->sasl_callbacks; cp->id != SASL_CB_LIST_END; cp++)
+ cp->context = (void *) state;
+- if (sasl_client_new("smtp", state->session->host,
++
++ if (SASL_CLIENT_NEW("smtp", state->session->host,
++ NULL, NULL,
+ state->sasl_callbacks, NULL_SECFLAGS,
+ (sasl_conn_t **) &state->sasl_conn) != SASL_OK)
+ msg_fatal("per-session SASL client initialization");
+@@ -354,7 +401,11 @@
+ char *myname = "lmtp_sasl_authenticate";
+ unsigned enc_length;
+ unsigned enc_length_out;
++#if SASL_VERSION_MAJOR >= 2
++ const char *clientout;
++#else
+ char *clientout;
++#endif
+ unsigned clientoutlen;
+ unsigned serverinlen;
+ LMTP_RESP *resp;
+@@ -374,7 +425,7 @@
+ /*
+ * Start the client side authentication protocol.
+ */
+- result = sasl_client_start((sasl_conn_t *) state->sasl_conn,
++ result = SASL_CLIENT_START((sasl_conn_t *) state->sasl_conn,
+ state->sasl_mechanism_list,
+ NO_SASL_SECRET, NO_SASL_INTERACTION,
+ &clientout, &clientoutlen, &mechanism);
+@@ -404,7 +455,10 @@
+ STR(state->sasl_encoded), enc_length,
+ &enc_length_out) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch", myname);
++#if SASL_VERSION_MAJOR < 2
++ /* SASL version 1 doesn't free memory that it allocates. */
+ free(clientout);
++#endif
+ lmtp_chat_cmd(state, "AUTH %s %s", mechanism, STR(state->sasl_encoded));
+ } else {
+ lmtp_chat_cmd(state, "AUTH %s", mechanism);
+@@ -423,8 +477,8 @@
+ (void) mystrtok(&line, "- \t\n"); /* skip over result code */
+ serverinlen = strlen(line);
+ VSTRING_SPACE(state->sasl_decoded, serverinlen);
+- if (sasl_decode64(line, serverinlen,
+- STR(state->sasl_decoded), &enc_length) != SASL_OK) {
++ if (SASL_DECODE64(line, serverinlen, STR(state->sasl_decoded),
++ serverinlen, &enc_length) != SASL_OK) {
+ vstring_sprintf(why, "malformed SASL challenge from server %s",
+ state->session->namaddr);
+ return (-1);
+@@ -456,7 +510,10 @@
+ STR(state->sasl_encoded), enc_length,
+ &enc_length_out) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch", myname);
++#if SASL_VERSION_MAJOR < 2
++ /* SASL version 1 doesn't free memory that it allocates. */
+ free(clientout);
++#endif
+ } else {
+ vstring_strcat(state->sasl_encoded, "");
+ }
+@@ -487,7 +544,8 @@
+ state->sasl_passwd = 0;
+ }
+ if (state->sasl_mechanism_list) {
+- myfree(state->sasl_mechanism_list); /* allocated in lmtp_helo */
++ /* state->sasl_mechanism_list is allocated in lmtp_sasl_helo_auth */
++ myfree(state->sasl_mechanism_list);
+ state->sasl_mechanism_list = 0;
+ }
+ if (state->sasl_conn) {
+diff -ru postfix-1.1.4-orig/src/smtp/smtp_sasl_glue.c postfix-1.1.4/src/smtp/smtp_sasl_glue.c
+--- postfix-1.1.4-orig/src/smtp/smtp_sasl_glue.c Mon Jul 2 14:12:54 2001
++++ postfix-1.1.4/src/smtp/smtp_sasl_glue.c Mon Mar 11 17:35:41 2002
+@@ -116,6 +116,9 @@
+ "noactive", SASL_SEC_NOACTIVE,
+ "nodictionary", SASL_SEC_NODICTIONARY,
+ "noanonymous", SASL_SEC_NOANONYMOUS,
++#if SASL_VERSION_MAJOR >= 2
++ "mutual_auth", SASL_SEC_MUTUAL_AUTH,
++#endif
+ 0,
+ };
+
+@@ -127,6 +130,44 @@
+ #define STR(x) vstring_str(x)
+
+ /*
++ * Macros to handle API differences between SASLv1 and SASLv2.
++ * Specifics:
++ * The SASL_LOG_* constants were renamed in SASLv2.
++ * SASLv2's sasl_client_new takes two new parameters to specify local
++ * and remote IP addresses for auth mechs that use them.
++ * SASLv2's sasl_client_start function no longer takes the secret parameter.
++ * SASLv2's sasl_decode64 function takes an extra parameter for the
++ * length of the output buffer.
++ *
++ * The other major change is that SASLv2 now takes more responsibility for
++ * deallocating memory that it allocates internally. Thus, some of the
++ * function parameters are now 'const', to make sure we don't try to free
++ * them too. This is dealt with in the code later on.
++ */
++
++#if SASL_VERSION_MAJOR < 2
++/* SASL version 1.x */
++#define SASL_LOG_WARN SASL_LOG_WARNING
++#define SASL_LOG_NOTE SASL_LOG_INFO
++#define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
++ sasl_client_new(srv, fqdn, prompt, secflags, pconn)
++#define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
++ sasl_client_start(conn, mechlst, secret, prompt, clout, cllen, mech)
++#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
++ sasl_decode64(in, inlen, out, outlen)
++#endif
++
++#if SASL_VERSION_MAJOR >= 2
++/* SASL version > 2.x */
++#define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
++ sasl_client_new(srv, fqdn, lport, rport, prompt, secflags, pconn)
++#define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
++ sasl_client_start(conn, mechlst, prompt, clout, cllen, mech)
++#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
++ sasl_decode64(in, inlen, out, outmaxlen, outlen)
++#endif
++
++ /*
+ * Per-host login/password information.
+ */
+ static MAPS *smtp_sasl_passwd_map;
+@@ -137,14 +178,18 @@
+ const char *message)
+ {
+ switch (priority) {
+- case SASL_LOG_ERR:
+- case SASL_LOG_WARNING:
++ case SASL_LOG_ERR: /* unusual errors */
++ case SASL_LOG_WARN: /* non-fatal warnings */
+ msg_warn("SASL authentication problem: %s", message);
+ break;
+- case SASL_LOG_INFO:
++ case SASL_LOG_NOTE: /* other info */
+ if (msg_verbose)
+ msg_info("SASL authentication info: %s", message);
+ break;
++#if SASL_VERSION_MAJOR >= 2
++ case SASL_LOG_FAIL: /* authentication failures - SASLv2 only */
++ msg_warn("SASL authentication failure: %s", message);
++#endif
+ }
+ return (SASL_OK);
+ }
+@@ -317,7 +362,9 @@
+ memcpy((char *) state->sasl_callbacks, callbacks, sizeof(callbacks));
+ for (cp = state->sasl_callbacks; cp->id != SASL_CB_LIST_END; cp++)
+ cp->context = (void *) state;
+- if (sasl_client_new("smtp", state->session->host,
++
++ if (SASL_CLIENT_NEW("smtp", state->session->host,
++ NULL, NULL,
+ state->sasl_callbacks, NULL_SECFLAGS,
+ (sasl_conn_t **) &state->sasl_conn) != SASL_OK)
+ msg_fatal("per-session SASL client initialization");
+@@ -354,7 +401,11 @@
+ char *myname = "smtp_sasl_authenticate";
+ unsigned enc_length;
+ unsigned enc_length_out;
++#if SASL_VERSION_MAJOR >= 2
++ const char *clientout;
++#else
+ char *clientout;
++#endif
+ unsigned clientoutlen;
+ unsigned serverinlen;
+ SMTP_RESP *resp;
+@@ -374,7 +425,7 @@
+ /*
+ * Start the client side authentication protocol.
+ */
+- result = sasl_client_start((sasl_conn_t *) state->sasl_conn,
++ result = SASL_CLIENT_START((sasl_conn_t *) state->sasl_conn,
+ state->sasl_mechanism_list,
+ NO_SASL_SECRET, NO_SASL_INTERACTION,
+ &clientout, &clientoutlen, &mechanism);
+@@ -404,7 +455,10 @@
+ STR(state->sasl_encoded), enc_length,
+ &enc_length_out) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch", myname);
++#if SASL_VERSION_MAJOR < 2
++ /* SASL version 1 doesn't free memory that it allocates. */
+ free(clientout);
++#endif
+ smtp_chat_cmd(state, "AUTH %s %s", mechanism, STR(state->sasl_encoded));
+ } else {
+ smtp_chat_cmd(state, "AUTH %s", mechanism);
+@@ -423,8 +477,8 @@
+ (void) mystrtok(&line, "- \t\n"); /* skip over result code */
+ serverinlen = strlen(line);
+ VSTRING_SPACE(state->sasl_decoded, serverinlen);
+- if (sasl_decode64(line, serverinlen,
+- STR(state->sasl_decoded), &enc_length) != SASL_OK) {
++ if (SASL_DECODE64(line, serverinlen, STR(state->sasl_decoded),
++ serverinlen, &enc_length) != SASL_OK) {
+ vstring_sprintf(why, "malformed SASL challenge from server %s",
+ state->session->namaddr);
+ return (-1);
+@@ -456,7 +510,10 @@
+ STR(state->sasl_encoded), enc_length,
+ &enc_length_out) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch", myname);
++#if SASL_VERSION_MAJOR < 2
++ /* SASL version 1 doesn't free memory that it allocates. */
+ free(clientout);
++#endif
+ } else {
+ vstring_strcat(state->sasl_encoded, "");
+ }
+@@ -487,7 +544,8 @@
+ state->sasl_passwd = 0;
+ }
+ if (state->sasl_mechanism_list) {
+- myfree(state->sasl_mechanism_list); /* allocated in smtp_helo */
++ /* state->sasl_mechanism_list is allocated in smtp_sasl_helo_auth */
++ myfree(state->sasl_mechanism_list);
+ state->sasl_mechanism_list = 0;
+ }
+ if (state->sasl_conn) {
+diff -ru postfix-1.1.4-orig/src/smtpd/smtpd.h postfix-1.1.4/src/smtpd/smtpd.h
+--- postfix-1.1.4-orig/src/smtpd/smtpd.h Mon Mar 11 00:44:45 2002
++++ postfix-1.1.4/src/smtpd/smtpd.h Mon Mar 11 14:04:58 2002
+@@ -69,7 +69,11 @@
+ off_t msg_size;
+ int junk_cmds;
+ #ifdef USE_SASL_AUTH
++# if SASL_VERSION_MAJOR >= 2
++ const char *sasl_mechanism_list;
++# else
+ char *sasl_mechanism_list;
++# endif
+ char *sasl_method;
+ char *sasl_username;
+ char *sasl_sender;
+diff -ru postfix-1.1.4-orig/src/smtpd/smtpd_sasl_glue.c postfix-1.1.4/src/smtpd/smtpd_sasl_glue.c
+--- postfix-1.1.4-orig/src/smtpd/smtpd_sasl_glue.c Sun Nov 25 18:14:01 2001
++++ postfix-1.1.4/src/smtpd/smtpd_sasl_glue.c Mon Mar 11 17:35:37 2002
+@@ -110,20 +110,69 @@
+ */
+ #define STR(s) vstring_str(s)
+
++ /*
++ * Macros to handle API differences between SASLv1 and SASLv2.
++ * Specifics:
++ * The SASL_LOG_* constants were renamed in SASLv2.
++ * SASLv2's sasl_server_new takes two new parameters to specify local
++ * and remote IP addresses for auth mechs that use them.
++ * SASLv2's sasl_server_start and sasl_server_step no longer have the
++ * errstr parameter.
++ * SASLv2's sasl_decode64 function takes an extra parameter for the
++ * length of the output buffer.
++ *
++ * The other major change is that SASLv2 now takes more responsibility for
++ * deallocating memory that it allocates internally. Thus, some of the
++ * function parameters are now 'const', to make sure we don't try to free
++ * them too. This is dealt with in the code later on.
++ */
++
++#if SASL_VERSION_MAJOR < 2
++/* SASL version 1.x */
++#define SASL_LOG_WARN SASL_LOG_WARNING
++#define SASL_LOG_NOTE SASL_LOG_INFO
++#define SASL_SERVER_NEW(srv, fqdn, rlm, lport, rport, cb, secflags, pconn) \
++ sasl_server_new(srv, fqdn, rlm, cb, secflags, pconn)
++#define SASL_SERVER_START(conn, mech, clin, clinlen, srvout, srvoutlen, err) \
++ sasl_server_start(conn, mech, clin, clinlen, srvout, srvoutlen, err)
++#define SASL_SERVER_STEP(conn, clin, clinlen, srvout, srvoutlen, err) \
++ sasl_server_step(conn, clin, clinlen, srvout, srvoutlen, err)
++#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
++ sasl_decode64(in, inlen, out, outlen)
++#endif
++
++#if SASL_VERSION_MAJOR >= 2
++/* SASL version > 2.x */
++#define SASL_SERVER_NEW(srv, fqdn, rlm, lport, rport, cb, secflags, pconn) \
++ sasl_server_new(srv, fqdn, rlm, lport, rport, cb, secflags, pconn)
++#define SASL_SERVER_START(conn, mech, clin, clinlen, srvout, srvoutlen, err) \
++ sasl_server_start(conn, mech, clin, clinlen, srvout, srvoutlen)
++#define SASL_SERVER_STEP(conn, clin, clinlen, srvout, srvoutlen, err) \
++ sasl_server_step(conn, clin, clinlen, srvout, srvoutlen)
++#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
++ sasl_decode64(in, inlen, out, outmaxlen, outlen)
++#endif
++
+ /* smtpd_sasl_log - SASL logging callback */
+
+ static int smtpd_sasl_log(void *unused_context, int priority,
+ const char *message)
+ {
+ switch (priority) {
+- case SASL_LOG_ERR:
+- case SASL_LOG_WARNING:
++ case SASL_LOG_ERR:
++ case SASL_LOG_WARN:
+ msg_warn("SASL authentication problem: %s", message);
+ break;
+- case SASL_LOG_INFO:
++ case SASL_LOG_NOTE:
+ if (msg_verbose)
+ msg_info("SASL authentication info: %s", message);
+ break;
++#if SASL_VERSION_MAJOR >= 2
++ case SASL_LOG_FAIL:
++ if (msg_verbose)
++ msg_info("SASL authentication failure: %s", message);
++ break;
++#endif
+ }
+ return SASL_OK;
+ }
+@@ -144,6 +193,9 @@
+ "noactive", SASL_SEC_NOACTIVE,
+ "nodictionary", SASL_SEC_NODICTIONARY,
+ "noanonymous", SASL_SEC_NOANONYMOUS,
++#if SASL_VERSION_MAJOR >= 2
++ "mutual_auth", SASL_SEC_MUTUAL_AUTH,
++#endif
+ 0,
+ };
+
+@@ -174,6 +226,9 @@
+ {
+ unsigned sasl_mechanism_count;
+ sasl_security_properties_t sec_props;
++ char *iplocal;
++ char *ipremote;
++
+
+ /*
+ * Initialize SASL-specific state variables. Use long-lived storage for
+@@ -195,11 +250,24 @@
+ #define NO_SECURITY_LAYERS (0)
+ #define NO_SESSION_CALLBACKS ((sasl_callback_t *) 0)
+
+- if (sasl_server_new("smtp", var_myhostname, var_smtpd_sasl_realm,
++
++#if SASL_VERSION_MAJOR >= 2 && defined(USE_SASL_IP_AUTH)
++ /* Get IP addresses of local and remote hosts to pass to SASL. */
++
++#else
++ /* Don't give any IP information to SASL. SASLv1 doesn't use it, and
++ * in SASLv2 this will disable any mechs that do.
++ */
++ iplocal = NULL;
++ ipremote = NULL;
++#endif
++
++ if (SASL_SERVER_NEW("smtp", var_myhostname, var_smtpd_sasl_realm,
++ iplocal, ipremote,
+ NO_SESSION_CALLBACKS, NO_SECURITY_LAYERS,
+ &state->sasl_conn) != SASL_OK)
+ msg_fatal("SASL per-connection server initialization");
+-
++
+ /*
+ * Security options. Some information can be found in the sasl.h include
+ * file. Disallow anonymous authentication; this is because the
+@@ -239,7 +307,10 @@
+ void smtpd_sasl_disconnect(SMTPD_STATE *state)
+ {
+ if (state->sasl_mechanism_list) {
++#if SASL_VERSION_MAJOR < 2
++ /* SASL version 1 doesn't free memory that it allocates. */
+ free(state->sasl_mechanism_list);
++#endif
+ state->sasl_mechanism_list = 0;
+ }
+ if (state->sasl_conn) {
+@@ -262,10 +333,18 @@
+ unsigned enc_length;
+ unsigned enc_length_out;
+ unsigned reply_len;
+- char *serverout = 0;
+ unsigned serveroutlen;
+ int result;
++
++#if SASL_VERSION_MAJOR < 2
++ char *serverout = 0;
++#else
++ const char *serverout = 0;
++#endif
++
++#if SASL_VERSION_MAJOR < 2
+ const char *errstr = 0;
++#endif
+
+ #define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
+
+@@ -288,8 +367,8 @@
+ reply_len = strlen(init_response);
+ VSTRING_SPACE(state->sasl_decoded, reply_len);
+ dec_buffer = STR(state->sasl_decoded);
+- if (sasl_decode64(init_response, reply_len,
+- dec_buffer, &dec_length) != SASL_OK)
++ if (SASL_DECODE64(init_response, reply_len,
++ dec_buffer, reply_len, &dec_length) != SASL_OK)
+ return ("501 Authentication failed: malformed initial response");
+ if (msg_verbose)
+ msg_info("%s: decoded initial response %s", myname, dec_buffer);
+@@ -297,7 +376,7 @@
+ dec_buffer = 0;
+ dec_length = 0;
+ }
+- result = sasl_server_start(state->sasl_conn, sasl_method, dec_buffer,
++ result = SASL_SERVER_START(state->sasl_conn, sasl_method, dec_buffer,
+ dec_length, &serverout, &serveroutlen, &errstr);
+
+ /*
+@@ -327,7 +406,10 @@
+ if (sasl_encode64(serverout, serveroutlen, STR(state->sasl_encoded),
+ enc_length, &enc_length_out) != SASL_OK)
+ msg_panic("%s: sasl_encode64 botch", myname);
++#if SASL_VERSION_MAJOR < 2
++ /* SASL version 1 doesn't free memory that it allocates. */
+ free(serverout);
++#endif
+ serverout = 0;
+ smtpd_chat_reply(state, "334 %s", STR(state->sasl_encoded));
+
+@@ -342,21 +424,24 @@
+ return ("501 Authentication aborted"); /* XXX */
+ reply_len = VSTRING_LEN(state->buffer);
+ VSTRING_SPACE(state->sasl_decoded, reply_len);
+- if (sasl_decode64(vstring_str(state->buffer), reply_len,
+- STR(state->sasl_decoded), &dec_length) != SASL_OK)
++ if (SASL_DECODE64(vstring_str(state->buffer), reply_len,
++ STR(state->sasl_decoded), reply_len,
++ &dec_length) != SASL_OK)
+ return ("501 Error: malformed authentication response");
+ if (msg_verbose)
+ msg_info("%s: decoded response: %.*s",
+ myname, (int) dec_length, STR(state->sasl_decoded));
+- result = sasl_server_step(state->sasl_conn, STR(state->sasl_decoded),
++ result = SASL_SERVER_STEP(state->sasl_conn, STR(state->sasl_decoded),
+ dec_length, &serverout, &serveroutlen, &errstr);
+ }
+
++#if SASL_VERSION_MAJOR < 2
+ /*
+ * Cleanup. What an awful interface.
+ */
+ if (serverout)
+ free(serverout);
++#endif
+
+ /*
+ * The authentication protocol was completed.
+@@ -369,8 +454,13 @@
+ * accounting purposes. For the sake of completeness we also record the
+ * authentication method that was used. XXX Do not free(serverout).
+ */
++#if SASL_VERSION_MAJOR >= 2
++ result = sasl_getprop(state->sasl_conn, SASL_USERNAME,
++ (const void **) &serverout);
++#else
+ result = sasl_getprop(state->sasl_conn, SASL_USERNAME,
+ (void **) &serverout);
++#endif
+ if (result != SASL_OK || serverout == 0)
+ msg_panic("%s: sasl_getprop SASL_USERNAME botch", myname);
+ state->sasl_username = mystrdup(serverout);
+
diff --git a/postfix/postfix.spec b/postfix/postfix.spec
new file mode 100644
index 0000000..c6971a5
--- /dev/null
+++ b/postfix/postfix.spec
@@ -0,0 +1,336 @@
+##
+## postfix.spec -- OpenPKG RPM Specification
+## Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH
+## Copyright (c) 2000-2002 The OpenPKG Project <http://www.openpkg.org/>
+## Copyright (c) 2000-2002 Ralf S. Engelschall <rse@engelschall.com>
+##
+## Permission to use, copy, modify, and distribute this software for
+## any purpose with or without fee is hereby granted, provided that
+## the above copyright notice and this permission notice appear in all
+## copies.
+##
+## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+## SUCH DAMAGE.
+##
+
+# involved versions
+%define V_postfix 1.1.11
+%define V_tls 0.8.11a-1.1.11-0.9.6g
+%define V_pflogsumm 1.0.4
+
+# build options
+%ifndef with_tls
+%define with_tls no
+%endif
+%ifndef with_sasl
+%define with_sasl no
+%endif
+%ifndef with_mysql
+%define with_mysql no
+%endif
+%ifndef with_fsl
+%define with_fsl no
+%endif
+
+# package information
+Name: postfix
+Summary: Mail Transfer Agent (MTA)
+URL: http://www.postfix.org/
+Vendor: Wietse Venema
+Packager: The OpenPKG Project
+Distribution: OpenPKG [BASE]
+Group: Mail
+License: IPL
+Version: %{V_postfix}
+Release: 20021014
+
+# list of sources
+Source0: ftp://ftp.porcupine.org/mirrors/postfix-release/official/postfix-%{V_postfix}.tar.gz
+Source1: ftp://ftp.aet.tu-cottbus.de/pub/postfix_tls/pfixtls-%{V_tls}.tar.gz
+Source2: fsl.postfix
+Source3: etc.tar
+Source4: http://jimsun.linxnet.com/downloads/pflogsumm-%{V_pflogsumm}.pl
+Source5: rc.postfix
+Source6: postfix-sasl2.patch
+#Patch0: postfix-db4.patch
+
+# build information
+Prefix: %{l_prefix}
+BuildRoot: %{l_buildroot}
+BuildPreReq: OpenPKG, openpkg >= 20020206, perl
+PreReq: OpenPKG, openpkg >= 20020206, perl, procmail, perl-time
+BuildPreReq: db, pcre
+%if "%{with_tls}" == "yes"
+BuildPreReq: openssl, patch
+%endif
+%if "%{with_sasl}" == "yes"
+BuildPreReq: sasl
+PreReq: sasl
+%endif
+%if "%{with_fsl}" == "yes"
+BuildPreReq: fsl
+PreReq: fsl
+%endif
+%if "%{with_mysql}" == "yes"
+BuildPreReq: mysql
+PreReq: mysql
+%endif
+AutoReq: no
+AutoReqProv: no
+Provides: MTA
+Conflicts: sendmail, ssmtp, exim
+
+%description
+ Postfix is a new-generation Mail Transfer Agent (MTA) able to
+ fully replace the Sendmail MTA.
+
+ Local specifics in this OpenPKG version:
+ o Postfix delivers locally via Procmail
+ o Postfix logs directly to the filesystem and not via syslog(3)
+ o Optional IPv6 support
+ o Optional STARTTLS support
+ o Optional Berkeley-DB lookup table support
+ o Optional MySQL support
+ o Optional PCRE matching support
+ o Optional SASL2 authentication support
+
+ Options:
+ --define 'with_mysql %{with_mysql}' \
+ --define 'with_sasl %{with_sasl}' \
+ --define 'with_tls %{with_tls}'
+
+%prep
+ %setup0 -q -c -a 0
+# %patch0 -p0
+%if "%{with_tls}" == "yes"
+ %setup1 -q -T -D -a 1
+%endif
+ %setup3 -q -T -D -a 3
+ cd postfix-%{V_postfix}
+%if "%{with_tls}" == "yes"
+ %{l_patch} -p1 < ../pfixtls-%{V_tls}/pfixtls.diff
+%endif
+%if "%{with_sasl}" == "yes"
+ %{l_patch} -p1 < %{SOURCE postfix-sasl2.patch}
+%endif
+ %{l_shtool} subst \
+ -e 's/var_config_dir, /var_command_dir, /' \
+ src/postfix/postfix.c
+ %{l_shtool} subst \
+ -e 's;config_directory/postfix-script;command_directory/postfix-script;' \
+ -e 's;config_directory/post-install;command_directory/postfix-install;' \
+ conf/postfix-script*
+ %{l_shtool} subst \
+ -e 's;/usr/include;%{l_prefix}/include;g' \
+ makedefs
+ %{l_shtool} subst \
+ -e 's;#define HAS_DBM;#define HAS_DBM_DISABLED;' \
+ -e 's;#define HAS_DB;#define HAS_DB_DISABLED;' \
+ src/util/sys_defs.h
+
+%build
+ # configure Postfix
+ cd postfix-%{V_postfix}
+ unset LD_LIBRARY_PATH || true
+ CCARGS=""
+ CCARGS="$CCARGS %{l_cflags -O}"
+ CCARGS="$CCARGS -I%{l_prefix}/include"
+ CCARGS="$CCARGS -DDEF_CONFIG_DIR=\\\\\\\"%{l_prefix}/etc/postfix\\\\\\\""
+ AUXLIBS=""
+ AUXLIBS="$AUXLIBS -L%{l_prefix}/lib"
+ CCARGS="$CCARGS -DHAS_DB"
+ AUXLIBS="$AUXLIBS -ldb"
+ CCARGS="$CCARGS -DHAS_PCRE"
+ AUXLIBS="$AUXLIBS -lpcre"
+%if "%{with_mysql}" == "yes"
+ CCARGS="$CCARGS -DHAS_MYSQL -I%{l_prefix}/include/mysql"
+ AUXLIBS="$AUXLIBS -L%{l_prefix}/lib/mysql -lmysqlclient -lz -lm"
+%endif
+%if "%{with_tls}" == "yes"
+ CCARGS="$CCARGS -DHAS_SSL"
+ AUXLIBS="$AUXLIBS -lssl -lcrypto"
+%endif
+%if "%{with_sasl}" == "yes"
+ CCARGS="$CCARGS -DUSE_SASL_AUTH -I%{l_prefix}/include/sasl"
+ AUXLIBS="$AUXLIBS -lsasl2"
+ if [ -f /usr/lib/libdl.so -o -f /usr/lib/libdl.a ]; then
+ AUXLIBS="$AUXLIBS -ldl"
+ fi
+%endif
+%if "%{with_fsl}" == "yes"
+ AUXLIBS="$AUXLIBS `%{l_prefix}/bin/fsl-config --all --ldflags --libs`"
+%endif
+ %{l_make} %{l_mflags} makefiles \
+ CC="%{l_cc}" CCARGS="$CCARGS" AUXLIBS="$AUXLIBS"
+
+ # build Postfix
+ %{l_make} %{l_mflags}
+
+%install
+ rm -rf $RPM_BUILD_ROOT
+
+ # perform standard installation procedure
+ (
+ cd postfix-%{V_postfix}
+ %{l_shtool} subst -e "s;chown;true;" postfix-install
+ sh postfix-install -non-interactive \
+ install_root=$RPM_BUILD_ROOT \
+ config_directory=%{l_prefix}/etc/postfix \
+ daemon_directory=%{l_prefix}/libexec/postfix \
+ command_directory=%{l_prefix}/sbin \
+ queue_directory=%{l_prefix}/var/postfix \
+ sendmail_path=%{l_prefix}/sbin/sendmail \
+ newaliases_path=%{l_prefix}/sbin/newaliases \
+ mailq_path=%{l_prefix}/sbin/mailq \
+ manpage_directory=%{l_prefix}/man \
+ mail_user=%{l_musr} \
+ setgid_group=%{l_rgrp}
+ %{l_shtool} install -c -m 755 \
+ -e 's;/usr/sbin/sendmail;%{l_prefix}/sbin/sendmail;g' \
+ auxiliary/rmail/rmail $RPM_BUILD_ROOT%{l_prefix}/sbin/rmail
+ )
+
+ # post-adjust binaries
+ rm -f $RPM_BUILD_ROOT%{l_prefix}/sbin/mailq
+ ln $RPM_BUILD_ROOT%{l_prefix}/sbin/sendmail \
+ $RPM_BUILD_ROOT%{l_prefix}/sbin/mailq
+ rm -f $RPM_BUILD_ROOT%{l_prefix}/sbin/newaliases
+ ln $RPM_BUILD_ROOT%{l_prefix}/sbin/sendmail \
+ $RPM_BUILD_ROOT%{l_prefix}/sbin/newaliases
+ strip $RPM_BUILD_ROOT%{l_prefix}/sbin/* >/dev/null 2>&1 || true
+ strip $RPM_BUILD_ROOT%{l_prefix}/libexec/postfix/* >/dev/null 2>&1 || true
+
+ # post-adjust configuration
+ %{l_shtool} subst \
+ -e "s;^\\(mail_owner[^=]*=\\).*;\\1 %{l_musr};" \
+ $RPM_BUILD_ROOT%{l_prefix}/etc/postfix/main.cf
+ rm -f $RPM_BUILD_ROOT%{l_prefix}/etc/postfix/*.orig
+ rm -f $RPM_BUILD_ROOT%{l_prefix}/etc/postfix/LICENSE
+ mv $RPM_BUILD_ROOT%{l_prefix}/etc/postfix/postfix-script \
+ $RPM_BUILD_ROOT%{l_prefix}/sbin/postfix-script
+ rm -f $RPM_BUILD_ROOT%{l_prefix}/etc/postfix/postfix-script*
+ mv $RPM_BUILD_ROOT%{l_prefix}/etc/postfix/post-install \
+ $RPM_BUILD_ROOT%{l_prefix}/sbin/postfix-install
+ ( cd $RPM_BUILD_ROOT%{l_prefix}/etc/postfix
+ %{l_shtool} mkdir -f -p -m 755 sample
+ for cfg in access aliases canonical pcre_table regexp_table \
+ relocated sample-* transport virtual \
+ main.cf master.cf main.cf.default; do
+ mv $cfg sample/
+ done
+ )
+
+ # install default configuration
+ %{l_shtool} install -c -m 644 \
+ -e 's;@l_prefix@;%{l_prefix};g' \
+ -e 's;@l_musr@;%{l_musr};g' \
+ -e 's;@l_mgrp@;%{l_mgrp};g' \
+ -e 's;@l_rusr@;%{l_rusr};g' \
+ -e 's;@l_rgrp@;%{l_rgrp};g' \
+ -e 's;@l_nusr@;%{l_nusr};g' \
+ -e 's;@l_ngrp@;%{l_ngrp};g' \
+ etc/* $RPM_BUILD_ROOT%{l_prefix}/etc/postfix/
+
+ # pre-create variable stuff
+ ( cd $RPM_BUILD_ROOT%{l_prefix}/var/postfix
+ %{l_shtool} mkdir -f -p -m 700 active bounce corrupt defer deferred \
+ flush incoming private saved
+ %{l_shtool} mkdir -f -p -m 730 maildrop
+ %{l_shtool} mkdir -f -p -m 710 public
+ %{l_shtool} mkdir -f -p -m 755 log pid
+ )
+
+ # install addons
+ %{l_shtool} install -c -m 755 \
+ -e 's;/usr/bin/perl;%{l_prefix}/bin/perl;g' \
+ %{SOURCE4} $RPM_BUILD_ROOT%{l_prefix}/sbin/pflogsumm
+
+ # install run-command script
+ %{l_shtool} mkdir -f -p -m 755 \
+ $RPM_BUILD_ROOT%{l_prefix}/etc/rc.d
+ %{l_shtool} install -c -m 755 \
+ -e 's;@l_prefix@;%{l_prefix};g' \
+ -e 's;@l_susr@;%{l_susr};g' \
+ -e 's;@l_musr@;%{l_musr};g' \
+ -e 's;@l_mgrp@;%{l_mgrp};g' \
+ %{SOURCE rc.postfix} $RPM_BUILD_ROOT%{l_prefix}/etc/rc.d/
+
+ # OSSP fsl support
+%if "%{with_fsl}" == "yes"
+ %{l_shtool} mkdir -f -p -m 755 \
+ $RPM_BUILD_ROOT%{l_prefix}/etc/fsl
+ %{l_shtool} install -c -m 644 \
+ -e 's;@l_prefix@;%{l_prefix};g' \
+ %{SOURCE fsl.postfix} \
+ $RPM_BUILD_ROOT%{l_prefix}/etc/fsl/
+%endif
+
+ # adjust installation to avoid file name conflicts
+ ( cd $RPM_BUILD_ROOT%{l_prefix}/man/man8
+ mv master.8 postfix_master.8
+ )
+
+ # generate file list
+ %{l_rpmtool} files -v -ofiles -r$RPM_BUILD_ROOT \
+ %{l_files_std} \
+ '%attr(-,root,%{l_mgrp}) %{l_prefix}/etc/postfix' \
+%if "%{with_fsl}" == "yes"
+ '%config %{l_prefix}/etc/fsl/fsl.postfix' \
+ '%not %dir %{l_prefix}/etc/fsl' \
+%endif
+ '%config %attr(-,root,%{l_mgrp}) %{l_prefix}/etc/postfix/*' \
+ '%attr(-,root,%{l_mgrp}) %{l_prefix}/libexec/postfix/*' \
+ '%attr(2755,%{l_musr},%{l_rgrp}) %{l_prefix}/sbin/{postdrop,postqueue}' \
+ '%dir %attr(-,root,%{l_mgrp}) %{l_prefix}/libexec/postfix' \
+ '%dir %attr(-,root,%{l_mgrp}) %{l_prefix}/var/postfix' \
+ '%dir %attr(-,%{l_musr},%{l_rgrp}) %{l_prefix}/var/postfix/{maildrop,public}'
+
+%files -f files
+
+%clean
+ rm -rf $RPM_BUILD_ROOT
+
+%post
+ if [ $1 -eq 1 ]; then
+ # generate logfile with correct owner and permissions
+ if [ ! -f $RPM_INSTALL_PREFIX/var/postfix/log/postfix.log ]; then
+ $RPM_INSTALL_PREFIX/lib/openpkg/shtool \
+ install -c -m 755 -o %{l_musr} -g %{l_mgrp} \
+ /dev/null $RPM_INSTALL_PREFIX/var/postfix/log/postfix.log
+ fi
+ # generate initial configuration
+ (cd $RPM_INSTALL_PREFIX/etc/postfix; %{l_make} all >/dev/null 2>&1 || true)
+ elif [ $1 -gt 1 ]; then
+ # re-genersate configuration
+ (cd $RPM_INSTALL_PREFIX/etc/postfix; %{l_make} clean all >/dev/null 2>&1 || true)
+ # re-load daemon
+ $RPM_INSTALL_PREFIX/etc/rc postfix reload >/dev/null 2>&1 || true
+ fi
+
+%preun
+ if [ $1 -eq 0 ]; then
+ # stop daemon
+ $RPM_INSTALL_PREFIX/etc/rc postfix stop >/dev/null 2>&1 || true
+ # remove generated configuration files
+ (cd $RPM_INSTALL_PREFIX/etc/postfix/; %{l_make} clean >/dev/null 2>&1 || true)
+ # remove generated run-time files and directories
+ rm -rf $RPM_INSTALL_PREFIX/etc/postfix/prng_exch
+ rm -rf $RPM_INSTALL_PREFIX/var/postfix/pid/*
+ rm -rf $RPM_INSTALL_PREFIX/var/postfix/private/*
+ rm -rf $RPM_INSTALL_PREFIX/var/postfix/public/*
+ find $RPM_INSTALL_PREFIX/var/postfix/active/ -type d -print |\
+ xargs rmdir >/dev/null 2>&1 || true
+ find $RPM_INSTALL_PREFIX/var/postfix/incoming/ -type d -print |\
+ xargs rmdir >/dev/null 2>&1 || true
+ fi
+
diff --git a/postfix/rc.postfix b/postfix/rc.postfix
new file mode 100644
index 0000000..3286a43
--- /dev/null
+++ b/postfix/rc.postfix
@@ -0,0 +1,60 @@
+#!@l_prefix@/lib/openpkg/bash @l_prefix@/etc/rc
+##
+## rc.postfix -- Run-Commands for Postfix Daemon
+##
+
+%config
+ mta_name="postfix"
+ mta_aliases_file="@l_prefix@/etc/postfix/aliases"
+ mta_aliases_update="cd @l_prefix@/etc/postfix && @l_prefix@/sbin/postalias aliases"
+ postfix_enable="yes"
+ postfix_log_prolog="true"
+ postfix_log_epilog="true"
+ postfix_log_numfiles="10"
+ postfix_log_minsize="1M"
+ postfix_log_complevel="9"
+ postfix_sum_flags=""
+
+%start -p 200 -u @l_susr@
+ opServiceEnabled postfix || exit 0
+ @l_prefix@/sbin/postfix start
+
+%stop -p 200 -u @l_susr@
+ opServiceEnabled postfix || exit 0
+ @l_prefix@/sbin/postfix stop
+
+%restart -u @l_susr@
+ opServiceEnabled postfix || exit 0
+ @l_prefix@/sbin/postfix stop
+ sleep 2
+ @l_prefix@/sbin/postfix start
+
+%reload -u @l_susr@
+ opServiceEnabled postfix || exit 0
+ @l_prefix@/sbin/postfix reload
+
+%daily -u @l_susr@
+ opServiceEnabled postfix || exit 0
+
+ # generate logfile summary
+ shtool rotate -f \
+ -n${postfix_log_numfiles} -s0 \
+ -z${postfix_log_complevel} -o@l_musr@ -g@l_mgrp@ -m644 \
+ @l_prefix@/var/postfix/log/postfix.sum
+ logfiles="@l_prefix@/var/postfix/log/postfix.log"
+ if [ -f "@l_prefix@/var/postfix/log/postfix.log.0" ]; then
+ logfiles="$logfiles @l_prefix@/var/postfix/log/postfix.log.0"
+ fi
+ @l_prefix@/sbin/pflogsumm -d yesterday -h 10 -u 10 -i \
+ --iso_date_time --problems_first --smtpd_stats --verbose_msg_detail \
+ ${postfix_sum_flags} \
+ ${logfiles} >@l_prefix@/var/postfix/log/postfix.sum
+
+ # rotate logfile
+ shtool rotate -f \
+ -n${postfix_log_numfiles} -s${postfix_log_minsize} -d \
+ -z${postfix_log_complevel} -o@l_musr@ -g@l_mgrp@ -m644 \
+ -P "$postfix_log_prolog" \
+ -E "@l_prefix@/sbin/postfix reload; $postfix_log_epilog" \
+ @l_prefix@/var/postfix/log/postfix.log
+