diff options
author | gshapiro <gshapiro@FreeBSD.org> | 2000-08-12 21:55:49 +0000 |
---|---|---|
committer | gshapiro <gshapiro@FreeBSD.org> | 2000-08-12 21:55:49 +0000 |
commit | 4332139a9a11f773ffe5109bed871561e3c290a1 (patch) | |
tree | 6d207932926718f38869bd08959330c09f4f3e0d /contrib/sendmail/contrib | |
parent | a392fe0bdb7081117c445f5dcc98d5ed4013dc17 (diff) | |
download | FreeBSD-src-4332139a9a11f773ffe5109bed871561e3c290a1.zip FreeBSD-src-4332139a9a11f773ffe5109bed871561e3c290a1.tar.gz |
Import of sendmail version 8.11.0 into vendor branch SENDMAIL with
release tag v8_11_0.
Obtained from: ftp://ftp.sendmail.org/pub/sendmail/
Diffstat (limited to 'contrib/sendmail/contrib')
-rw-r--r-- | contrib/sendmail/contrib/README | 4 | ||||
-rw-r--r-- | contrib/sendmail/contrib/bitdomain.c | 14 | ||||
-rwxr-xr-x | contrib/sendmail/contrib/bounce-resender.pl | 282 | ||||
-rw-r--r-- | contrib/sendmail/contrib/bsdi.mc | 2 | ||||
-rwxr-xr-x | contrib/sendmail/contrib/cidrexpand | 137 | ||||
-rw-r--r-- | contrib/sendmail/contrib/domainmap.m4 | 90 | ||||
-rwxr-xr-x | contrib/sendmail/contrib/etrn.pl | 51 | ||||
-rwxr-xr-x | contrib/sendmail/contrib/expn.pl | 2 | ||||
-rw-r--r-- | contrib/sendmail/contrib/link_hash.sh | 36 | ||||
-rw-r--r-- | contrib/sendmail/contrib/movemail.conf | 35 | ||||
-rwxr-xr-x | contrib/sendmail/contrib/movemail.pl | 106 | ||||
-rwxr-xr-x | contrib/sendmail/contrib/passwd-to-alias.pl | 13 | ||||
-rw-r--r-- | contrib/sendmail/contrib/qtool.8 | 206 | ||||
-rwxr-xr-x | contrib/sendmail/contrib/qtool.pl | 1190 | ||||
-rw-r--r-- | contrib/sendmail/contrib/re-mqueue.pl | 87 | ||||
-rwxr-xr-x | contrib/sendmail/contrib/smcontrol.pl | 6 |
16 files changed, 2208 insertions, 53 deletions
diff --git a/contrib/sendmail/contrib/README b/contrib/sendmail/contrib/README index dcf5c8f..1098f48 100644 --- a/contrib/sendmail/contrib/README +++ b/contrib/sendmail/contrib/README @@ -5,6 +5,6 @@ for assistance. Some of these are patches to sendmail itself. You may need to take care -- some of the patches may be out of date with the latest release of sendmail. Also, the previous comment applies -- patches belong to -the original author, not to me. +the original author, not to us. -Eric Allman, 26 May 1993 +$Revision: 8.2 $, Last updated $Date: 1999/09/24 05:46:47 $ diff --git a/contrib/sendmail/contrib/bitdomain.c b/contrib/sendmail/contrib/bitdomain.c index 52d6d21..0b7073d 100644 --- a/contrib/sendmail/contrib/bitdomain.c +++ b/contrib/sendmail/contrib/bitdomain.c @@ -9,11 +9,11 @@ * Change directory to "netinfo" and get the file internet.listing * The file is updated monthly. * - * Feed the output of this program to "makemap hash /etc/bitdomain.db" + * Feed the output of this program to "makemap hash /etc/mail/bitdomain.db" * to create the table used by the "FEATURE(bitdomain)" config file macro. * If your sendmail does not have the db library compiled in, you can instead - * use "makemap dbm /etc/bitdomain" and - * "FEATURE(bitdomain,`dbm -o /etc/bitdomain')" + * use "makemap dbm /etc/mail/bitdomain" and + * "FEATURE(bitdomain,`dbm -o /etc/mail/bitdomain')" * * The bitdomain table should be rebuilt monthly. */ @@ -51,7 +51,7 @@ char **argv; { int opt; - while ((opt = getopt(argc, argv, "o:")) != EOF) { + while ((opt = getopt(argc, argv, "o:")) != -1) { switch (opt) { case 'o': if (!freopen(optarg, "w", stdout)) { @@ -187,7 +187,7 @@ char *domainlen; case NO_DATA: err = "registered in DNS, but not mailable"; break; - + default: err = "unknown nameserver error"; break; @@ -210,7 +210,7 @@ valhost(host, hbsize) int hbsize; { register u_char *eom, *ap; - register int n; + register int n; HEADER *hp; querybuf answer; int ancount, qdcount; @@ -406,4 +406,4 @@ finish() } } } - + diff --git a/contrib/sendmail/contrib/bounce-resender.pl b/contrib/sendmail/contrib/bounce-resender.pl new file mode 100755 index 0000000..9253cdd --- /dev/null +++ b/contrib/sendmail/contrib/bounce-resender.pl @@ -0,0 +1,282 @@ +#!/usr/local/bin/perl -w +# +# bounce-resender: constructs mail queue from bounce spool for +# subsequent reprocessing by sendmail +# +# usage: given a mail spool full of (only) bounced mail called "bounces": +# # mkdir -m0700 bqueue; cd bqueue && bounce-resender < ../bounces +# # cd .. +# # chown -R root bqueue; chmod 600 bqueue/* +# # /usr/lib/sendmail -bp -oQ`pwd`/bqueue | more # does it look OK? +# # /usr/lib/sendmail -q -oQ`pwd`/bqueue -oT99d & # run the queue +# +# ** also read messages at end! ** +# +# Brian R. Gaeke <brg@EECS.Berkeley.EDU> Thu Feb 18 13:40:10 PST 1999 +# +############################################################################# +# This script has NO WARRANTY, NO BUG FIXES, and NO SUPPORT. You will +# need to modify it for your site and for your operating system, unless +# you are in the EECS Instructional group at UC Berkeley. (Search forward +# for two occurrences of "FIXME".) +# + +$state = "MSG_START"; +$ctr = 0; +$lineno = 0; +$getnrl = 0; +$nrl = ""; +$uname = "PhilOS"; # You don't want to change this here. +$myname = $0; +$myname =~ s,.*/([^/]*),$1,; + +chomp($hostname = `hostname`); +chomp($uname = `uname`); + +# FIXME: Define the functions "major" and "minor" for your OS. +if ($uname eq "SunOS") { + # from h2ph < /usr/include/sys/sysmacros.h on + # SunOS torus.CS.Berkeley.EDU 5.6 Generic_105182-11 i86pc i386 i86pc + eval 'sub O_BITSMINOR () {8;}' unless defined(&O_BITSMINOR); + eval 'sub O_MAXMAJ () {0x7f;}' unless defined(&O_MAXMAJ); + eval 'sub O_MAXMIN () {0xff;}' unless defined(&O_MAXMIN); + eval 'sub major { + local($x) = @_; + eval "((($x) >> &O_BITSMINOR) &O_MAXMAJ)"; + }' unless defined(&major); + eval 'sub minor { + local($x) = @_; + eval "(($x) &O_MAXMIN)"; + }' unless defined(&minor); +} else { + die "How do you calculate major and minor device numbers on $uname?\n"; +} + +sub ignorance { $ignored{$state}++; } + +sub unmunge { + my($addr) = @_; + $addr =~ s/_FNORD_/ /g; + # remove (Real Name) + $addr =~ s/^(.*)\([^\)]*\)(.*)$/$1$2/ + if $addr =~ /^.*\([^\)]*\).*$/; + # extract <user@host> if it appears + $addr =~ s/^.*<([^>]*)>.*$/$1/ + if $addr =~ /^.*<[^>]*>.*$/; + # strip leading, trailing blanks + $addr =~ s/^\s*(.*)\s*/$1/; + # nuke local domain + # FIXME: Add a regular expression for your local domain here. + $addr =~ + s/@(cory|po|pasteur|torus|parker|cochise|franklin).(ee)?cs.berkeley.edu//i; + return $addr; +} + +print STDERR "$0: running on $hostname ($uname)\n"; + +open(INPUT,$ARGV[0]) || die "$ARGV[0]: $!\n"; + +sub working { + my($now); + $now = localtime; + print STDERR "$myname: Working... $now\n"; +} + +&working(); + +while (! eof INPUT) { + # get a new line + if ($state eq "IN_MESSAGE_HEADER") { + # handle multi-line headers + if ($nrl ne "" || $getnrl != 0) { + $_ = $nrl; + $getnrl = 0; + $nrl = ""; + } else { + $_ = <INPUT>; $lineno++; + } + unless ($_ =~ /^\s*$/) { + while ($nrl eq "") { + $nrl = <INPUT>; $lineno++; + if ($nrl =~ /^\s+[^\s].*$/) { # continuation line + chomp($_); + $_ .= "_FNORD_" . $nrl; + $nrl = ""; + } elsif ($nrl =~ /^\s*$/) { # end of headers + $getnrl++; + last; + } + } + } + } else { + # normal single line + if ($nrl ne "") { + $_ = $nrl; $nrl = ""; + } else { + $_ = <INPUT>; $lineno++; + } + } + + if ($state eq "WAIT_FOR_FROM") { + if (/^From \S+.*$/) { + $state = "MSG_START"; + } else { + &ignorance(); + } + } elsif ($state eq "MSG_START") { + if (/^\s+boundary=\"([^\"]*)\".*$/) { + $boundary = $1; + $state = "GOT_BOUNDARY"; + $ctr++; + } else { + &ignorance(); + } + } elsif ($state eq "GOT_BOUNDARY") { + if (/^--$boundary/) { + $next = <INPUT>; $lineno++; + if ($next =~ /^Content-Type: message\/rfc822/) { + $hour = (localtime)[2]; + $char = chr(ord("A") + $hour); + $ident = sprintf("%sAA%05d",$char,99999 - $ctr); + $qf = "qf$ident"; + $df = "df$ident"; + @rcpt = (); + open(MSGHDR,">$qf") || die "Can't write to $qf: $!\n"; + open(MSGBODY,">$df") || die "Can't write to $df: $!\n"; + chmod(0600, $qf, $df); + $state = "IN_MESSAGE_HEADER"; + $header = $body = ""; + $messageid = "bounce-resender-$ctr"; + $fromline = "MAILER-DAEMON"; + $ctencod = "7BIT"; + # skip a bit, brother maynard (boundary is separated from + # the header by a blank line) + $next = <INPUT>; $lineno++; + unless ($next =~ /^\s*$/) { + print MSGHDR $next; + } + } + } else { + &ignorance(); + } + + $next = $char = $hour = undef; + } elsif ($state eq "IN_MESSAGE_HEADER") { + if (!(/^--$boundary/ || /^\s*$/)) { + if (/^Message-[iI][dD]:\s+<([^@]+)@[^>]*>.*$/) { + $messageid = $1; + } elsif (/^From:\s+(.*)$/) { + $fromline = $sender = $1; + $fromline = unmunge($fromline); + } elsif (/^Content-[Tt]ransfer-[Ee]ncoding:\s+(.*)$/) { + $ctencod = $1; + } elsif (/^(To|[Cc][Cc]):\s+(.*)$/) { + $toaddrs = $2; + foreach $toaddr (split(/,/,$toaddrs)) { + $toaddr = unmunge($toaddr); + push(@rcpt,$toaddr); + } + } + $headerline = $_; + # escape special chars + # (Perhaps not. It doesn't seem to be necessary (yet)). + #$headerline =~ s/([\(\)<>@,;:\\".\[\]])/\\$1/g; + # purely heuristic ;-) + $headerline =~ s/Return-Path:/?P?Return-Path:/g; + # save H-line to write to qf, later + $header .= "H$headerline"; + + $headerline = $toaddr = $toaddrs = undef; + } elsif (/^\s*$/) { + # write to qf + ($dev, $ino) = (stat($df))[0 .. 1]; + ($maj, $min) = (major($dev), minor($dev)); + $time = time(); + print MSGHDR "V2\n"; + print MSGHDR "B$ctencod\n"; + print MSGHDR "S$sender\n"; + print MSGHDR "I$maj/$min/$ino\n"; + print MSGHDR "K$time\n"; + print MSGHDR "T$time\n"; + print MSGHDR "D$df\n"; + print MSGHDR "N1\n"; + print MSGHDR "MDeferred: manually-requeued bounced message\n"; + foreach $r (@rcpt) { + print MSGHDR "RP:$r\n"; + } + $header =~ s/_FNORD_/\n/g; + print MSGHDR $header; + print MSGHDR "HMessage-ID: <$messageid@$hostname>\n" + if ($messageid =~ /bounce-resender/); + print MSGHDR ".\n"; + close MSGHDR; + + # jump to state waiting for message body + $state = "IN_MESSAGE_BODY"; + + $dev = $ino = $maj = $min = $r = $time = undef; + } elsif (/^--$boundary/) { + # signal an error + print "$myname: Header without message! Line $lineno qf $qf\n"; + + # write to qf anyway (SAME AS ABOVE, SHOULD BE A PROCEDURE) + ($dev, $ino) = (stat($df))[0 .. 1]; + ($maj, $min) = (major($dev), minor($dev)); + $time = time(); + print MSGHDR "V2\n"; + print MSGHDR "B$ctencod\n"; + print MSGHDR "S$sender\n"; + print MSGHDR "I$maj/$min/$ino\n"; + print MSGHDR "K$time\n"; + print MSGHDR "T$time\n"; + print MSGHDR "D$df\n"; + print MSGHDR "N1\n"; + print MSGHDR "MDeferred: manually-requeued bounced message\n"; + foreach $r (@rcpt) { + print MSGHDR "RP:$r\n"; + } + $header =~ s/_FNORD_/\n/g; + print MSGHDR $header; + print MSGHDR "HMessage-ID: <$messageid@$hostname>\n" + if ($messageid =~ /bounce-resender/); + print MSGHDR ".\n"; + close MSGHDR; + + # jump to state waiting for next bounce message + $state = "WAIT_FOR_FROM"; + + $dev = $ino = $maj = $min = $r = $time = undef; + } else { + # never got here + &ignorance(); + } + } elsif ($state eq "IN_MESSAGE_BODY") { + if (/^--$boundary/) { + print MSGBODY $body; + close MSGBODY; + $state = "WAIT_FOR_FROM"; + } else { + $body .= $_; + } + } + if ($lineno % 1900 == 0) { &working(); } +} + +close INPUT; + +foreach $x (keys %ignored) { + print STDERR + "$myname: ignored $ignored{$x} lines of bounce spool in state $x\n"; +} +print STDERR + "$myname: processed $lineno lines of input and wrote $ctr messages\n"; +print STDERR + "$myname: remember to chown the queue files to root before running:\n"; +chomp($pwd = `pwd`); +print STDERR "$myname: # sendmail -q -oQ$pwd -oT99d &\n"; + +print STDERR "$myname: to test the newly generated queue:\n"; +print STDERR "$myname: # sendmail -bp -oQ$pwd | more\n"; + +exit 0; + diff --git a/contrib/sendmail/contrib/bsdi.mc b/contrib/sendmail/contrib/bsdi.mc index 231a7bc..5175a34 100644 --- a/contrib/sendmail/contrib/bsdi.mc +++ b/contrib/sendmail/contrib/bsdi.mc @@ -35,7 +35,7 @@ and examples describing most of the common things people need to setup. # See /usr/share/sendmail/README for help in building a configuration file. # include(`../m4/cf.m4') -VERSIONID(`@(#)$Id$') +VERSIONID(`@(#)$Id: bsdi.mc,v 8.1 1999/02/06 18:44:08 gshapiro Exp $') dnl # Specify your OS type below OSTYPE(`bsd4.4') diff --git a/contrib/sendmail/contrib/cidrexpand b/contrib/sendmail/contrib/cidrexpand new file mode 100755 index 0000000..b61fc2e --- /dev/null +++ b/contrib/sendmail/contrib/cidrexpand @@ -0,0 +1,137 @@ +#!/usr/local/bin/perl -w + +# v 0.2-very-very-beta +# +# 17 July 2000 Derek J. Balling (dredd@megacity.org) +# +# The $SENDMAIL flag tells the code to lump networks in sendmail format +# if applicable. If this flag is disabled, cidrexpand will literally create +# a single line for each entry, which may or may not be what you want. :) +# makes for a rather large hash table... +# +# Acts as a preparser on /etc/mail/access_db to allow you to use address/bit +# notation. Caveat: the address portion MUST be the start address or your +# results will NOT be what what you want. +# +# +# usage: +# cidrexpand < /etc/mail/access | makemap hash /etc/mail/access +# +# +# Report bugs to: dredd@megacity.org +# + +my $spaceregex = '\s+'; + +while (my $arg = shift @ARGV) +{ + if ($arg eq '-t') + { + $spaceregex = shift; + } +} + +use strict; + +my $SENDMAIL = 1; + +while (<>) +{ + my ($left,$right,$space); + + if (! /^(\d+\.){3}\d+\/\d\d?$spaceregex.*/ ) + { + print; + } + else + { + ($left,$space,$right) = /^((?:\d+\.){3}\d+\/\d\d?)($spaceregex)(.*)$/; + + my @new_lefts = expand_network($left); + foreach my $nl (@new_lefts) + { + print "$nl$space$right\n"; + } + + } +} + +sub expand_network +{ + my ($network,$mask) = split /\//, shift; + my @diffs = calc_changes($network,$mask); + my ($first,$second,$third,$fourth) = split /\./, $network; + + my @rc = (); + + for my $f ($first..($first+$diffs[0])) + { + if ( ( $SENDMAIL ) and ($diffs[1] == 255)) + { + push @rc, "$f"; + } + else + { + for my $s ($second..($second+$diffs[1])) + { + if ( ($SENDMAIL) and ($diffs[2] == 255) ) + { + push @rc,"$f\.$s"; + } + else + { + for my $t ($third..($third+$diffs[2])) + { + if ( ($SENDMAIL) and ($diffs[3] == 255)) + { + push @rc, "$f\.$s\.$t"; + } + else + { + for my $fr ($fourth..($fourth+$diffs[3])) + { + push @rc, "$f\.$s\.$t\.$fr"; + } + } + } + } + } + } + } + return @rc; +} + +sub calc_changes +{ + my ($network,$mask) = @_; + + my @octs = split /\./, $network; + + my ($first,$second,$third,$fourth) = (0,0,0,0); + + my $power = 32 - $mask; + + if ($mask > 24) + { + $fourth = 2**$power - 1; + } + elsif ($mask > 16) + { + $fourth = 255; + $third = 2**($power-8) - 1; + } + elsif ($mask > 8) + { + $fourth = 255; + $third = 255; + $second = 2**($power-16) - 1; + } + elsif ($mask > 0) + { + $fourth = 255; + $third = 255; + $second = 255; + $first = 2**($power-24) - 1; + } + return ($first,$second,$third,$fourth); +} diff --git a/contrib/sendmail/contrib/domainmap.m4 b/contrib/sendmail/contrib/domainmap.m4 new file mode 100644 index 0000000..31d284c --- /dev/null +++ b/contrib/sendmail/contrib/domainmap.m4 @@ -0,0 +1,90 @@ +divert(-1)changequote(<<, >>)<< +----------------------------------------------------------------------------- + + FEATURE(domainmap) Macro + + The existing virtusertable feature distributed with sendmail is a good + basic approach to virtual hosting, but it is missing a few key + features: + + 1. Ability to have a different map for each domain. + 2. Ability to perform virtual hosting for domains which are not in $=w. + 3. Ability to use a centralized network-accessible database (such as + PH) which is keyed on username alone (as opposed to the + fully-qualified email address). + + The FEATURE(domainmap) macro neatly solves these problems. + + The basic syntax of the macro is: + FEATURE(domainmap, `domain.com', `map definition ...')dnl + + To illustrate how it works, here is an example: + FEATURE(domainmap, `foo.com', `dbm -o /etc/mail/foo-users')dnl + + In this example, mail sent to user@foo.com will be rewritten by the + domainmap. The username will be looked up in the DBM map + /etc/mail/foo-users, which looks like this: + jsmith johnsmith@mailbox.foo.com + jdoe janedoe@sandbox.bar.com + + So mail sent to jsmith@foo.com will be relayed to + johnsmith@mailbox.foo.com, and mail sent to jdoe@foo.com will be + relayed to janedoe@sandbox.bar.com. + + The FEATURE(domainmap) Macro supports the user+detail syntax by + stripping off the +detail portion before the domainmap lookup and + tacking it back on to the result. Using the example above, mail sent + to jsmith+sometext@foo.com will be rewritten as + johnsmith+sometext@mailbox.foo.com. + + If one of the elements in the $=w class (i.e., "local" delivery hosts) + is a domain specified in a FEATURE(domainmap) entry, you need to use + the LOCAL_USER(username) macro to specify the list of users for whom + domainmap lookups should not be done. + + To use this macro, simply copy this file into the cf/feature directory + in the sendmail source tree. For more information, please see the + following URL: + + http://www-wsg.cso.uiuc.edu/sendmail/patches/domainmap.html + + Feedback is welcome. + + Mark D. Roth <roth@uiuc.edu> + +----------------------------------------------------------------------------- +>>changequote(`, ')undivert(-1)divert + +ifdef(`_DOMAIN_MAP_',`',`dnl +LOCAL_RULE_0 +# do mapping for domains where applicable +R$* $=O $* <@ $={MappedDomain} .> $@ $>97 $1 $2 $3 Strip extraneous routing +R$+ <@ $={MappedDomain} .> $>DomainMapLookup $1 <@ $2 .> domain mapping + +LOCAL_RULESETS +########################################################################### +### Ruleset DomainMapLookup -- special rewriting for mapped domains ### +########################################################################### + +SDomainMapLookup +R $=L <@ $=w .> $@ $1 <@ $2 .> weed out local users, in case +# Cw contains a mapped domain +R $+ <@ $+ .> $1 <@ $2 > strip trailing dot +R $+ <@ $+ . $+ > $1 <@ $(dequote $2 "_" $3 $) > +# change "." to "_" +R $+ <@ $+ > $: $1 <@ $(dequote "domain_" $2 $) > +# prepend "domain_" +R $+ + $+ <@ $*> $1 <@ $3 > <+> $2 handle user+list syntax +R $+ <@ $* > $* $( $2 $1 $: <ERROR> $) $3 +# do actual domain map lookup +R <ERROR> $* $#error $@ 5.1.1 $: "550 email address lookup in domain map failed" +R $* <TEMP> $* $#error $@ 4.3.0 $: "450 domain map temporarily unavailable" +R $+ @ $+ <+> $+ $1 + $3 @ $2 reset original user+list +R $+ <+> $* $1 paranoid check - remove <+> +R $+ @ $+ . $1 @ $2 strip trailing dot +R $+ @ $+ $@ $>97 $1 @ $2 recanonify +define(`_DOMAIN_MAP_',`1')') + +LOCAL_CONFIG +C{MappedDomain} _ARG_ +K `domain_'translit(_ARG_, `.', `_') _ARG2_ -T<TEMP> diff --git a/contrib/sendmail/contrib/etrn.pl b/contrib/sendmail/contrib/etrn.pl index 1e2cba9..2dfb58d 100755 --- a/contrib/sendmail/contrib/etrn.pl +++ b/contrib/sendmail/contrib/etrn.pl @@ -15,7 +15,7 @@ $sockaddr = 'S n a4 x8'; # must have 'hostname' program. ############################################################################# -# Copyright (c) 1996 John T. Beck <john@beck.org> +# Copyright (c) 1996-2000 John T. Beck <john@beck.org> # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -60,7 +60,7 @@ $0 = "$av0 - running hostname"; chop($name = `hostname || uname -n`); $0 = "$av0 - lookup host FQDN and IP addr"; -($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name); +($hostname,$aliases,$type,$len,undef) = gethostbyname($name); $0 = "$av0 - parsing args"; $usage = "Usage: $av0 [-wd] host [args]"; @@ -71,12 +71,13 @@ $server = shift(@ARGV); @hosts = @ARGV; die $usage unless $server; @cwfiles = (); +$alarm_action = ""; if (!@hosts) { push(@hosts,$hostname); $0 = "$av0 - parsing sendmail.cf"; - open(CF, "</etc/sendmail.cf") || die "open /etc/sendmail.cf: $!"; + open(CF, "</etc/mail/sendmail.cf") || die "open /etc/mail/sendmail.cf: $!"; while (<CF>){ if (/^Fw.*$/){ # look for a line starting with "Fw" $cwfile = $_; @@ -125,6 +126,7 @@ $0 = "$av0 - building local socket"; $0 = "$av0 - gethostbyname($server)"; ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server); +(!defined($name)) && die "gethostbyname failed, unknown host $server"; # get a connection $0 = "$av0 - socket to $server"; @@ -132,22 +134,24 @@ $that = pack($sockaddr, &AF_INET, $port, $thataddr); socket(S, &AF_INET, &SOCK_STREAM, $proto) || die "socket: $!"; $0 = "$av0 - connect to $server"; -print "debug = $debug server = $server\n" if $debug > 8; +print "debug = $debug server = $server\n" if (defined($debug) && $debug > 8); +&alarm("connect to $server"); if (! connect(S, $that)) { - $0 = "$av0 - $server: could not connect: $!\n"; + die "cannot connect to $server: $!\n"; } +alarm(0); select((select(S),$| = 1)[0]); # don't buffer output to S # read the greeting $0 = "$av0 - talking to $server"; -&alarm("greeting with $server",''); +&alarm("greeting with $server"); while(<S>) { alarm(0); print if $watch; if (/^(\d+)([- ])/) { if ($1 != 220) { $0 = "$av0 - bad numeric response from $server"; - &alarm("giving up after bad response from $server",''); + &alarm("giving up after bad response from $server"); &read_response($2,$watch); alarm(0); print STDERR "$server: NOT 220 greeting: $_" @@ -160,13 +164,13 @@ while(<S>) { if ($debug || $watch); close(S); } - &alarm("greeting with $server",''); + &alarm("greeting with $server"); } alarm(0); # if this causes problems, remove it $0 = "$av0 - sending helo to $server"; -&alarm("sending ehlo to $server",""); +&alarm("sending ehlo to $server"); &ps("ehlo $hostname"); $etrn_support = 0; while(<S>) { @@ -180,7 +184,7 @@ alarm(0); if ($etrn_support){ print "ETRN supported\n" if ($debug); - &alarm("sending etrn to $server",''); + &alarm("sending etrn to $server"); while (@hosts) { $server = shift(@hosts); &ps("etrn $server"); @@ -194,7 +198,7 @@ if ($etrn_support){ print "\nETRN not supported\n\n" } -&alarm("sending 'quit' to $server",''); +&alarm("sending 'quit' to $server"); $0 = "$av0 - sending 'quit' to $server"; &ps("quit"); while(<S>) { @@ -217,14 +221,25 @@ sub ps sub alarm { - local($alarm_action,$alarm_redirect,$alarm_user) = @_; - alarm(3600); + ($alarm_action) = @_; + alarm(10); $SIG{ALRM} = 'handle_alarm'; } sub handle_alarm { - &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user); + &giveup($alarm_action); +} + +sub giveup +{ + local($reason) = @_; + local($pk,$file,$line); + ($pk, $file, $line) = caller; + + $0 = "$av0 - giving up on $server: $reason"; + print "Timed out during $reason\n" if $debug; + exit(1); } # read the rest of the current smtp daemon's response (and toss it away) @@ -241,9 +256,9 @@ sub read_response return @resp; } # to pass perl -w: -@tp; -$flag_a; -$flag_d; +my $x; +$x=$opt_d; +$x=$opt_w; &handle_alarm; ################### BEGIN PERL/TROFF TRANSITION .00 ; @@ -300,7 +315,7 @@ it is possible to eliminate bugs. .SH ENVIRONMENT No enviroment variables are used. .SH FILES -.B /etc/sendmail.cf +.B /etc/mail/sendmail.cf .SH SEE ALSO .BR sendmail (8), RFC 1985. diff --git a/contrib/sendmail/contrib/expn.pl b/contrib/sendmail/contrib/expn.pl index 57f8515..dd777e6 100755 --- a/contrib/sendmail/contrib/expn.pl +++ b/contrib/sendmail/contrib/expn.pl @@ -12,7 +12,7 @@ use IO::Socket; # system requirements: # must have 'nslookup' and 'hostname' programs. -# $Header: /home/muir/bin/RCS/expn,v 3.11 1997/09/10 08:14:02 muir Exp muir $ +# $OrigHeader: /home/muir/bin/RCS/expn,v 3.11 1997/09/10 08:14:02 muir Exp muir $ # TODO: # less magic should apply to command-line addresses diff --git a/contrib/sendmail/contrib/link_hash.sh b/contrib/sendmail/contrib/link_hash.sh new file mode 100644 index 0000000..e07104d --- /dev/null +++ b/contrib/sendmail/contrib/link_hash.sh @@ -0,0 +1,36 @@ +#!/bin/sh +## +## Copyright (c) 2000 Sendmail, Inc. and its suppliers. +## All rights reserved. +## +## $Id: link_hash.sh,v 1.1.2.1 2000/04/25 00:10:47 ca Exp $ +## +# +# ln a certificate to its hash +# +SSL=openssl +if test $# -ge 1 +then + for i in $@ + do + C=$i.pem + test -f $C || C=$i + if test -f $C + then + H=`$SSL x509 -noout -hash < $C`.0 + if test -h $H -o -f $H + then + echo link $H to $C exists + else + ln -s $C $H + fi + else + echo "$0: cannot open $C" + exit 2 + fi + done +else + echo "$0: missing name" + exit 1 +fi +exit 0 diff --git a/contrib/sendmail/contrib/movemail.conf b/contrib/sendmail/contrib/movemail.conf new file mode 100644 index 0000000..17009b8 --- /dev/null +++ b/contrib/sendmail/contrib/movemail.conf @@ -0,0 +1,35 @@ +# Configuration script for movemail.pl + +my $minutes = 60; +my $hours = 3600; + +# Queue directories first..last + +@queues = qw( + /var/spool/mqueue/q1 + /var/spool/mqueue/q2 + /var/spool/mqueue/q3 +); + +# Base of subqueue name (optional). +# If used, queue directories are $queues[n]/$subqbase* +# Separate qf/df/xf directories are not supported. + +$subqbase = "subq"; + +# Age of mail when moved. Each element of the array must be greater than the +# previous element. + +@ages = ( + 30*$minutes, # q1 to q2 + 6*$hours # q2 to q3 +); + +# Location of script to move the mail + +$remqueue = "/usr/local/bin/re-mqueue.pl"; + +# Lock file to prevent more than one instance running (optional) +# Useful when running from cron + +$lockfile = "/var/spool/mqueue/movemail.lock"; diff --git a/contrib/sendmail/contrib/movemail.pl b/contrib/sendmail/contrib/movemail.pl new file mode 100755 index 0000000..86bcb20 --- /dev/null +++ b/contrib/sendmail/contrib/movemail.pl @@ -0,0 +1,106 @@ +#!/usr/bin/perl -w +# +# Move old mail messages between queues by calling re-mqueue.pl. +# +# movemail.pl [config-script] +# +# Default config script is /usr/local/etc/movemail.conf. +# +# Graeme Hewson <graeme.hewson@oracle.com>, June 2000 +# + +use strict; + +# Load external program as subroutine to avoid +# compilation overhead on each call + +sub loadsub { + my $fn = shift + or die "Filename not specified"; + my $len = (stat($fn))[7] + or die "Can't stat $fn: $!"; + open PROG, "< $fn" + or die "Can't open $fn: $!"; + my $prog; + read PROG, $prog, $len + or die "Can't read $fn: $!"; + close PROG; + eval join "", + 'return sub { my @ARGV = @_; $0 = $fn; no strict;', + "$prog", + '};'; +} + +my $progname = $0; +my $lastage = -1; +my $LOCK_EX = 2; +my $LOCK_NB = 4; + +# Load and eval config script + +my $conffile = shift || "/usr/local/etc/movemail.conf"; +my $len = (stat($conffile))[7] + or die "Can't stat $conffile: $!"; +open CONF, "< $conffile" + or die "Can't open $conffile: $!"; +my $conf; +read CONF, $conf, $len + or die "Can't read $conffile: $!"; +close CONF; +use vars qw(@queues $subqbase @ages $remqueue $lockfile); +eval $conf; + +if ($#queues < 1) { + print "$progname: there must be at least two queues\n"; + exit 1; +} + +if ($#ages != ($#queues - 1)) { + print "$progname: wrong number of ages (should be one less than number of queues)\n"; + exit 1; +} + +# Get lock or exit quietly. Useful when running from cron. + +if ($lockfile) { + open LOCK, ">>$lockfile" + or die "Can't open lock file: $!"; + unless (flock LOCK, $LOCK_EX|$LOCK_NB) { + close LOCK; + exit 0; + } +} + +my $remsub = loadsub($remqueue); + +# Go through directories in reverse order so as to check spool files only once + +for (my $n = $#queues - 1; $n >= 0; $n--) { + unless ($ages[$n] =~ /^\d+$/) { + print "$progname: invalid number $ages[$n] in ages array\n"; + exit 1; + } + unless ($lastage < 0 || $ages[$n] < $lastage) { + print "$progname: age $lastage is not > previous value $ages[$n]\n"; + exit 1; + } + $lastage = $ages[$n]; + if ($subqbase) { + my $subdir; + opendir(DIR, $queues[$n]) + or die "Can't open $queues[$n]: $!"; + foreach $subdir ( grep { /^$subqbase/ } readdir DIR) { + &$remsub("$queues[$n]/$subdir", "$queues[$n+1]/$subdir", + $ages[$n]); + } + closedir(DIR); + } else { + # Not using subdirectories + &$remsub($queues[$n], $queues[$n+1], $ages[$n]); + } +} + +if ($lockfile) { + unlink $lockfile; + close LOCK; +} diff --git a/contrib/sendmail/contrib/passwd-to-alias.pl b/contrib/sendmail/contrib/passwd-to-alias.pl index 05a51b9..24bb7a1 100755 --- a/contrib/sendmail/contrib/passwd-to-alias.pl +++ b/contrib/sendmail/contrib/passwd-to-alias.pl @@ -8,22 +8,23 @@ print "# Generated from passwd by $0\n"; +$wordpat = '([a-zA-Z]+?[a-zA-Z0-9-]*)?[a-zA-Z0-9]'; # 'DB2' while (@a = getpwent) { ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = @a; ($fullname = $gcos) =~ s/,.*$//; - if (!-d $dir || !-x $shell) { - print "$name: root\n"; + if (!-d $dir || !-x $shell || $shell =~ m!/bin/(false|true)$!) { + print "$name: root\n"; # handle pseudo user } $fullname =~ s/\.*[ _]+\.*/./g; - $fullname =~ tr [åäöÅÄÖé] [aaoAAOe]; # <hakan@af.lu.se> 1997-06-15 - if ($fullname =~ /^[a-zA-Z][a-zA-Z-]+(\.[a-zA-Z][a-zA-Z-]+)+$/) { -# if ($fullname =~ /^[a-zA-Z]+(\.[a-zA-Z]+)+$/) { # Kari E. Hurtta + $fullname =~ tr [åäéöüÅÄÖÜ] [aaeouAAOU]; # <hakan@af.lu.se> 1997-06-15 + next if (!$fullname || lc($fullname) eq $name); # avoid nonsense + if ($fullname =~ /^$wordpat(\.$wordpat)*$/o) { # Ulrich Windl print "$fullname: $name\n"; } else { - print "# $fullname: $name\n"; + print "# $fullname: $name\n"; # avoid strange names } }; diff --git a/contrib/sendmail/contrib/qtool.8 b/contrib/sendmail/contrib/qtool.8 new file mode 100644 index 0000000..4d0f1c4 --- /dev/null +++ b/contrib/sendmail/contrib/qtool.8 @@ -0,0 +1,206 @@ +.\" Copyright (c) 1999 Sendmail, Inc. and its suppliers. +.\" All rights reserved. +.\" +.\" By using this file, you agree to the terms and conditions set +.\" forth in the LICENSE file which can be found at the top level of +.\" the sendmail distribution. +.\" +.\" +.\" $Id: qtool.8,v 8.9 1999/08/26 00:04:10 cying Exp $ +.\" +.TH QTOOL 8 "July 12, 1999" +.SH NAME +.B qtool +\- manipulate sendmail queues +.SH SYNOPSIS +.B qtool.pl +.RB [options] +target_directory source [source ...] +.PP +.B qtool.pl [-d/-b] +.RB [options] +source [source ...] +.SH DESCRIPTION +.B Qtool +moves the queue files used by sendmail between queues. It uses the same +locking mechanism as sendmail so can be safely used while sendmail is +running. +.PP +With no options, +.B qtool +will move any queue files as specified by \fIsource\fP into +\fItarget_directory\fP. \fISource\fP can be either an individual +queue control file, a queue file id, or a queue directory. +.PP +If the -d option is specified, qtool will delete the messages specified by +source instead of moving them. +.PP +If the -b option is specified, the selected messages will be bounced by +running sendmail with the -OTimeout.queuereturn=now option. +.SS Options +.TP +\fB\-b\fP +Bounce all of the messages specified by source. The messages will be bounced +immediately. No attempt will be made to deliver the messages. +.TP +\fB\-d\fP +Delete all of the messages specified by source. +.TP +\fB\-e\fP \fIperl_expression\fP +Evalute \fIperl_expression\fP for each queue file as specified +by \fIsource\fP. If \fIperl_expression\fP evaluates to true, then that +queue file is moved. See below for more detail on \fIperl_expression\fP. +.TP +\fB\-s\fP \fIseconds\fP +Move only the queue files specified by \fIsource\fP that have a +modification time older than \fIseconds\fP. +.SS Perl Expressions +You can use any valid perl expression. Inside the expression you have +access to a hash that contains many of the fields in the control file as +well as some other data about that queued message. The hash is called +\fI%msg\fP. If a field has multiple values (e.g. 'Recipient'), it will be +returned as an array, otherwise it will be returned as a scalar. Through +\fI%msg\fP, you can access the following variables: +.TP +\fBauth\fP +AUTH= parameter. +.TP +\fBbody_type\fP +Body type (\fB8BITMIME\fP, \fB7BIT\fP, or undefined). +.TP +\fBbody_last_mod_time\fP +The last time the body was modified since the epoch in seconds. +.TP +\fBbody_size\fP +The size of the body file in bytes. +.TP +\fBcharset\fP +Character set (for future use). +.TP +\fBcontent-length\fP +Content-Length: header value (Solaris sendmail only). +.TP +\fBcontrolling_user\fP +The controlling user. +.TP +\fBcontrol_last_mod_time\fP +The last time the body was modified since the epoch in seconds. +.TP +\fBcontrol_size\fP +The size of the control file in bytes. +.TP +\fBcreation_time\fP +The time when the control file was created. +.TP +\fBdata_file_name\fP +The data file name (deprecated). +.TP +\fBenvid\fP +Original envelope id form ESMTP. +.TP +\fBerror_recipient\fP +The error recipient (deprecated). +.TP +\fBflags\fP +Array of characters that can be the following values: +.PD 0 +.RS +8 +.TP 8 +w +warning message has been sent +.TP 8 +r +This is an error respone or DSN +.TP 8 +8 +has 8 bit data in body +.TP 8 +b +delete Bcc: headers +.TP 8 +d +envelope has DSN RET= parameter +.TP 8 +n +don't return body +.PD +.RE +.TP +\fBheaders\fP +This is a Perl hash where the keys are rfc822 field names and the values +are rfc822 field values. If a field has only one value it will be returned +as a string. If a field has more than one value (e.g. 'Received') it will +be returned as a list of strings. +.TP +\fBinode_number\fP +The inode number for the data (body) file. +.TP +\fBnext_delivery_time\fP +Earliest time of next delivery attempt. +.TP +\fBnum_delivery_attempts\fP +Number of delivery attempts that have been made. +.TP +\fBmacro\fP +Defined macro. +.TP +\fBmessage\fP +Envelope status message. +.TP +\fBoriginal_recipient\fP +Original recipient (ORCPT= parameter). +.TP +\fBpriority\fP +Adjusted priority of message. +.TP +\fBrecipient\fP +Array of character flags followed by colon and recipient name. Flags: +.PD 0 +.RS +8 +.TP 8 +N +Has NOTIFY= parameter. +.TP 8 +S +Success DSN requested. +.TP 8 +F +Failure DSN requested. +.TP 8 +D +Delay DSN requested. +.TP 8 +P +Primary address (not the result of alias/forward expansion). +.PD +.RE +.TP +\fBsender\fP +Sender +.TP +\fBversion\fP +Version of control file. +.SH EXAMPLES +.TP +\fBqtool.pl q2 q1\fP +Moves all of the queue files in queue q1 to queue q2. +.TP +\fBqtool.pl q2 q1/d6CLQh100847\fP +Moves the message with id d6CLQh100847 in queue q1 to queue q2. +.TP +\fBqtool.pl q2 q1/qfd6CLQh100847\fP +Moves the message with id d6CLQh100847 in queue q1 to queue q2. +.TP +\fBqtool.pl q2 q1/dfd6CLQh100847\fP +Moves the message with id d6CLQh100847 in queue q1 to queue q2. +.TP +\fBqtool.pl -e '$msg{num_delivery_attempts} == 3' /q2 /q1\fP +Moves all of the queue files that have had three attempted deliveries from +queue q1 to queue q2. +.SH SEE ALSO +sendmail(8) +.SH HISTORY +The +.B qtool +command appeared in +sendmail 8.10. diff --git a/contrib/sendmail/contrib/qtool.pl b/contrib/sendmail/contrib/qtool.pl new file mode 100755 index 0000000..0219fb5 --- /dev/null +++ b/contrib/sendmail/contrib/qtool.pl @@ -0,0 +1,1190 @@ +#!/usr/bin/env perl +## +## Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. +## All rights reserved. +## +## $Id: qtool.pl,v 8.15.16.1 2000/04/25 03:44:31 gshapiro Exp $ +## +use strict; +use File::Basename; +use File::Copy; +use File::Spec; +use Fcntl qw(:flock :DEFAULT); +use Getopt::Std; + +## +## QTOOL +## This program is for moving files between sendmail queues. It is +## pretty similar to just moving the files manually, but it locks the files +## the same way sendmail does to prevent problems. +## +## The syntax is the reverse of mv (ie. the target argument comes +## first). This lets you pick the files you want to move using find and +## xargs. +## +## Since you cannot delete queues while sendmail is running, QTOOL +## assumes that when you specify a directory as a source, you mean that you +## want all of the queue files within that directory moved, not the +## directory itself. +## +## There is a mechanism for adding conditionals for moving the files. +## Just create an Object with a check_move(source, dest) method and add it +## to the $conditions object. See the handling of the '-s' option for an +## example. +## + +## +## OPTION NOTES +## +## The -e option: +## The -e option takes any valid perl expression and evaluates it +## using the eval() function. Inside the expression the variable +## '$msg' is bound to the ControlFile object for the current source +## queue message. This lets you check for any value in the message +## headers or the control file. Here's an example: +## +## ./qtool.pl -e '$msg->{num_delivery_attempts} >= 2' /q1 /q2 +## +## This would move any queue files whose number of delivery attempts +## is greater than or equal to 2 from the queue 'q2' to the queue 'q1'. +## +## See the function ControlFile::parse for a list of available +## variables. +## + +my %opts; +my %sources; +my $dst_name; +my $destination; +my $source_name; +my $source; +my $result; +my $action; +my $new_condition; +my $conditions = new Compound(); + +Getopt::Std::getopts('bde:s:', \%opts); + +sub move_action +{ + my $source = shift; + my $destination = shift; + + $result = $destination->add($source); + if ($result) + { + print("$result.\n"); + } +} + +sub delete_action +{ + my $source = shift; + + return $source->delete(); +} + +sub bounce_action +{ + my $source = shift; + + return $source->bounce(); +} + +$action = \&move_action; +if (defined $opts{d}) +{ + $action = \&delete_action; +} +elsif (defined $opts{b}) +{ + $action = \&bounce_action; +} + +if (defined $opts{s}) +{ + $new_condition = new OlderThan($opts{s}); + $conditions->add($new_condition); +} + +if (defined $opts{e}) +{ + $new_condition = new Eval($opts{e}); + $conditions->add($new_condition); +} + +if ($action == \&move_action) +{ + $dst_name = shift(@ARGV); + if (!-d $dst_name) + { + print("The destination '$dst_name' must be an existing " . + "directory.\n"); + usage(); + exit; + } + $destination = new Queue($dst_name); +} + +while (@ARGV) +{ + $source_name = shift(@ARGV); + $result = add_source(\%sources, $source_name); + if ($result) + { + print("$result.\n"); + } +} + +if (keys(%sources) == 0) +{ + print("You must at least specify at least one source.\n"); + usage(); + exit; +} + +while (($source_name, $source) = each(%sources)) +{ + $result = $conditions->check_move($source, $destination); + if ($result) + { + $result = &{$action}($source, $destination); + if ($result) + { + print("$result\n"); + } + } +} + +sub usage +{ + print("Usage: $0 [options] directory source ...\n"); + print(" $0 [-d|-b] source ...\n"); + print("options:\n"); + print(" -b Bounce the messages specified by source.\n"); + print(" -d Delete the messages specified by source.\n"); + print(" -e [perl expression] Move only messages for which perl expression returns true.\n"); + print(" -s [seconds] Move only messages older than seconds.\n"); +} + +## +## ADD_SOURCE -- Adds a source to the source hash. +## +## Determines whether source is a file, directory, or id. Then it +## creates a QueuedMessage or Queue for that source and adds it to the +## list. +## +## Parameters: +## sources -- A hash that contains all of the sources. +## source_name -- The name of the source to add +## +## Returns: +## error_string -- Undef if ok. Error string otherwise. +## +## Notes: +## If a new source comes in with the same ID as a previous +## source, the previous source gets overwritten in the sources +## hash. This lets the user specify things like * and it still +## works nicely. +## + +sub add_source +{ + my $sources = shift; + my $source_name = shift; + my $source_base_name; + my $source_dir_name; + my $data_dir_name; + my $source_id; + my $source_prefix; + my $queued_message; + my $queue; + my $result; + + ($source_base_name, $source_dir_name) = File::Basename::fileparse($source_name); + $data_dir_name = $source_dir_name; + + $source_prefix = substr($source_base_name, 0, 2); + if (!-d $source_name && $source_prefix ne 'qf' && + $source_prefix ne 'df') + { + $source_base_name = "qf$source_base_name"; + $source_name = File::Spec->catfile("$source_dir_name", + "$source_base_name"); + } + $source_id = substr($source_base_name, 2); + + if (!-e $source_name) + { + $source_name = File::Spec->catfile("$source_dir_name", "qf", + "qf$source_id"); + if (!-e $source_name) + { + return "'$source_name' does not exist"; + } + $data_dir_name = File::Spec->catfile("$source_dir_name", "df"); + $source_dir_name = File::Spec->catfile("$source_dir_name", + "qf"); + } + + if (-f $source_name) + { + $queued_message = new QueuedMessage($source_dir_name, + $source_id, + $data_dir_name); + $sources->{$source_id} = $queued_message; + return undef; + } + + if (!-d $source_name) + { + return "'$source_name' is not a plain file or a directory"; + } + + $queue = new Queue($source_name); + $result = $queue->read(); + if ($result) + { + return $result; + } + + while (($source_id, $queued_message) = each(%{$queue->{files}})) + { + $sources->{$source_id} = $queued_message; + } + + return undef; +} + +## +## LOCK_FILE -- Opens and then locks a file. +## +## Opens a file for read/write and uses flock to obtain a lock on the +## file. The flock is Perl's flock which defaults to flock on systems +## that support it. On systems without flock it falls back to fcntl +## locking. +## +## Parameters: +## file_name -- The name of the file to open and lock. +## +## Returns: +## (file_handle, error_string) -- If everything works then +## file_handle is a reference to a file handle and +## error_string is undef. If there is a problem then +## file_handle is undef and error_string is a string +## explaining the problem. +## + +sub lock_file +{ + my $file_name = shift; + my $result; + + $result = sysopen(FILE_TO_LOCK, $file_name, Fcntl::O_RDWR); + if (!$result) + { + return (undef, "Unable to open '$file_name': $!"); + } + + $result = flock(FILE_TO_LOCK, Fcntl::LOCK_EX | Fcntl::LOCK_NB); + if (!$result) + { + return (undef, "Could not obtain lock on '$file_name': $!"); + } + + return (\*FILE_TO_LOCK, undef); +} + +## +## UNLOCK_FILE -- Unlocks a file. +## +## Unlocks a file using Perl's flock. +## +## Parameters: +## file -- A file handle. +## +## Returns: +## error_string -- If undef then no problem. Otherwise it is a +## string that explains problem. +## + +sub unlock_file +{ + my $file = shift; + my $result; + + $result = flock($file, Fcntl::LOCK_UN); + if (!$result) + { + return "Unlock failed on '$result': $!"; + } + + return undef; +} + +## +## MOVE_FILE -- Moves a file. +## +## Moves a file. +## +## Parameters: +## src_name -- The name of the file to be move. +## dst_nome -- The name of the place to move it to. +## +## Returns: +## error_string -- If undef then no problem. Otherwise it is a +## string that explains problem. +## + +sub move_file +{ + my $src_name = shift; + my $dst_name = shift; + my $result; + + $result = File::Copy::move($src_name, $dst_name); + if (!$result) + { + return "File move from '$src_name' to '$dst_name' failed: $!"; + } + + return undef; +} + + +## +## CONTROL_FILE - Represents a sendmail queue control file. +## +## This object represents represents a sendmail queue control file. +## It can parse and lock its file. +## + + +package ControlFile; + +sub new +{ + my $this = shift; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->initialize(@_); + return $self; +} + +sub initialize +{ + my $self = shift; + my $queue_dir = shift; + $self->{id} = shift; + + $self->{file_name} = $queue_dir . '/qf' . $self->{id}; + $self->{headers} = {}; +} + +## +## PARSE - Parses the control file. +## +## Parses the control file. It just sticks each entry into a hash. +## If a key has more than one entry, then it points to a list of +## entries. +## + +sub parse +{ + my $self = shift; + if ($self->{parsed}) + { + return; + } + my %parse_table = + ( + 'A' => 'auth', + 'B' => 'body_type', + 'C' => 'controlling_user', + 'D' => 'data_file_name', + 'E' => 'error_recipient', + 'F' => 'flags', + 'H' => 'parse_header', + 'I' => 'inode_number', + 'K' => 'next_delivery_time', + 'L' => 'content-length', + 'M' => 'message', + 'N' => 'num_delivery_attempts', + 'P' => 'priority', + 'Q' => 'original_recipient', + 'R' => 'recipient', + 'S' => 'sender', + 'T' => 'creation_time', + 'V' => 'version', + 'X' => 'charset', + 'Z' => 'envid', + '$' => 'macro' + ); + my $line; + my $line_type; + my $line_value; + my $member_name; + my $member; + my $last_type; + + open(CONTROL_FILE, "$self->{file_name}"); + while ($line = <CONTROL_FILE>) + { + $line_type = substr($line, 0, 1); + if ($line_type eq "\t" && $last_type eq 'H') + { + $line_type = 'H'; + $line_value = $line; + } + else + { + $line_value = substr($line, 1); + } + $member_name = $parse_table{$line_type}; + $last_type = $line_type; + if (!$member_name) + { + $member_name = 'unknown'; + } + if ($self->can($member_name)) + { + $self->$member_name($line_value); + } + $member = $self->{$member_name}; + if (!$member) + { + $self->{$member_name} = $line_value; + next; + } + if (ref($member) eq 'ARRAY') + { + push(@{$member}, $line_value); + next; + } + $self->{$member_name} = [$member, $line_value]; + } + close(CONTROL_FILE); + + $self->{parsed} = 1; +} + +sub parse_header +{ + my $self = shift; + my $line = shift; + my $headers = $self->{headers}; + my $last_header = $self->{last_header}; + my $header_name; + my $header_value; + my $first_char; + + $first_char = substr($line, 0, 1); + if ($first_char eq "?") + { + $line = substr($line, 3); + } + elsif ($first_char eq "\t") + { + if (ref($headers->{$last_header}) eq 'ARRAY') + { + $headers->{$last_header}[-1] = + $headers->{$last_header}[-1] . $line; + } + else + { + $headers->{$last_header} = $headers->{$last_header} . + $line; + } + return; + } + ($header_name, $header_value) = split(/:/, $line, 2); + $self->{last_header} = $header_name; + if (exists $headers->{$header_name}) + { + $headers->{$header_name} = [$headers->{$header_name}, + $header_value]; + } + else + { + $headers->{$header_name} = $header_value; + } +} + +sub is_locked +{ + my $self = shift; + + return (defined $self->{lock_handle}); +} + +sub lock +{ + my $self = shift; + my $lock_handle; + my $result; + + if ($self->is_locked()) + { + # Already locked + return undef; + } + + ($lock_handle, $result) = ::lock_file($self->{file_name}); + if (!$lock_handle) + { + return $result; + } + + $self->{lock_handle} = $lock_handle; + + return undef; +} + +sub unlock +{ + my $self = shift; + my $result; + + if (!$self->is_locked()) + { + # Not locked + return undef; + } + + $result = ::unlock_file($self->{lock_handle}); + + $self->{lock_handle} = undef; + + return $result; +} + +sub do_stat +{ + my $self = shift; + my $result; + my @result; + + $result = open(QUEUE_FILE, $self->{file_name}); + if (!$result) + { + return "Unable to open '$self->{file_name}': $!"; + } + @result = stat(QUEUE_FILE); + if (!@result) + { + return "Unable to stat '$self->{file_name}': $!"; + } + $self->{control_size} = $result[7]; + $self->{control_last_mod_time} = $result[9]; +} + +sub DESTROY +{ + my $self = shift; + + $self->unlock(); +} + +sub delete +{ + my $self = shift; + my $result; + + $result = unlink($self->{file_name}); + if (!$result) + { + return "Unable to delete $self->{file_name}: $!"; + } + return undef; +} + + +## +## DATA_FILE - Represents a sendmail queue data file. +## +## This object represents represents a sendmail queue data file. +## It is really just a place-holder. +## + +package DataFile; + +sub new +{ + my $this = shift; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->initialize(@_); + return $self; +} + +sub initialize +{ + my $self = shift; + my $queue_dir = shift; + $self->{id} = shift; + + $self->{file_name} = $queue_dir . '/df' . $self->{id}; +} + +sub do_stat +{ + my $self = shift; + my $result; + my @result; + + $result = open(QUEUE_FILE, $self->{file_name}); + if (!$result) + { + return "Unable to open '$self->{file_name}': $!"; + } + @result = stat(QUEUE_FILE); + if (!@result) + { + return "Unable to stat '$self->{file_name}': $!"; + } + $self->{body_size} = $result[7]; + $self->{body_last_mod_time} = $result[9]; +} + +sub delete +{ + my $self = shift; + my $result; + + $result = unlink($self->{file_name}); + if (!$result) + { + return "Unable to delete $self->{file_name}: $!"; + } + return undef; +} + + +## +## QUEUED_MESSAGE - Represents a queued sendmail message. +## +## This keeps track of the files that make up a queued sendmail +## message. +## Currently it has 'control_file' and 'data_file' as members. +## +## You can tie it to a fetch only hash using tie. You need to +## pass a reference to a QueuedMessage as the third argument +## to tie. +## + +package QueuedMessage; + +sub new +{ + my $this = shift; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->initialize(@_); + return $self; +} + +sub initialize +{ + my $self = shift; + my $queue_dir = shift; + my $id = shift; + my $data_dir = shift; + + $self->{id} = $id; + $self->{control_file} = new ControlFile($queue_dir, $id); + if ($data_dir) + { + $self->{data_file} = new DataFile($data_dir, $id); + } + else + { + $self->{data_file} = new DataFile($queue_dir, $id); + } +} + +sub last_modified_time +{ + my $self = shift; + my @result; + @result = stat($self->{data_file}->{file_name}); + return $result[9]; +} + +sub TIEHASH +{ + my $this = shift; + my $class = ref($this) || $this; + my $self = shift; + return $self; +} + +sub FETCH +{ + my $self = shift; + my $key = shift; + + if (exists $self->{control_file}->{$key}) + { + return $self->{control_file}->{$key}; + } + if (exists $self->{data_file}->{$key}) + { + return $self->{data_file}->{$key}; + } + + return undef; +} + +sub lock +{ + my $self = shift; + + return $self->{control_file}->lock(); +} + +sub unlock +{ + my $self = shift; + + return $self->{control_file}->unlock(); +} + +sub move +{ + my $self = shift; + my $destination = shift; + my $df_dest; + my $qf_dest; + my $result; + + $result = $self->lock(); + if ($result) + { + return $result; + } + + $qf_dest = File::Spec->catfile($destination, "qf"); + if (-d $qf_dest) + { + $df_dest = File::Spec->catfile($destination, "df"); + if (!-d $df_dest) + { + $df_dest = $destination; + } + } + else + { + $qf_dest = $destination; + $df_dest = $destination; + } + + if (-e File::Spec->catfile($qf_dest, "qf$self->{id}")) + { + $result = "There is already a queued message with id '$self->{id}' in '$destination'"; + } + + if (!$result) + { + $result = ::move_file($self->{data_file}->{file_name}, + $df_dest); + } + + if (!$result) + { + $result = ::move_file($self->{control_file}->{file_name}, + $qf_dest); + } + + $self->unlock(); + + return $result; +} + +sub parse +{ + my $self = shift; + + return $self->{control_file}->parse(); +} + +sub do_stat +{ + my $self = shift; + + $self->{control_file}->do_stat(); + $self->{data_file}->do_stat(); +} + +sub setup_vars +{ + my $self = shift; + + $self->parse(); + $self->do_stat(); +} + +sub delete +{ + my $self = shift; + my $result; + + $result = $self->{control_file}->delete(); + if ($result) + { + return $result; + } + $result = $self->{data_file}->delete(); + if ($result) + { + return $result; + } + + return undef; +} + +sub bounce +{ + my $self = shift; + my $command; + + $command = "sendmail -qI$self->{id} -O Timeout.queuereturn=now"; +# print("$command\n"); + system($command); +} + +## +## QUEUE - Represents a queued sendmail queue. +## +## This manages all of the messages in a queue. +## + +package Queue; + +sub new +{ + my $this = shift; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->initialize(@_); + return $self; +} + +sub initialize +{ + my $self = shift; + + $self->{queue_dir} = shift; + $self->{files} = {}; +} + +## +## READ - Loads the queue with all of the objects that reside in it. +## +## This reads the queue's directory and creates QueuedMessage objects +## for every file in the queue that starts with 'qf'. +## + +sub read +{ + my $self = shift; + my @control_files; + my $queued_message; + my $file_name; + my $id; + my $result; + my $control_dir; + my $data_dir; + + $control_dir = File::Spec->catfile($self->{queue_dir}, 'qf'); + + if (-e $control_dir) + { + $data_dir = File::Spec->catfile($self->{queue_dir}, 'df'); + if (!-e $data_dir) + { + $data_dir = $self->{queue_dir}; + } + } + else + { + $data_dir = $self->{queue_dir}; + $control_dir = $self->{queue_dir}; + } + + $result = opendir(QUEUE_DIR, $control_dir); + if (!$result) + { + return "Unable to open directory '$control_dir'"; + } + + @control_files = grep { /^qf.*/ && -f "$control_dir/$_" } readdir(QUEUE_DIR); + closedir(QUEUE_DIR); + foreach $file_name (@control_files) + { + $id = substr($file_name, 2); + $queued_message = new QueuedMessage($control_dir, $id, + $data_dir); + $self->{files}->{$id} = $queued_message; + } + + return undef; +} + + +## +## ADD_QUEUED_MESSAGE - Adds a QueuedMessage to this Queue. +## +## Adds the QueuedMessage object to the hash and moves the files +## associated with the QueuedMessage to this Queue's directory. +## + +sub add_queued_message +{ + my $self = shift; + my $queued_message = shift; + my $result; + + $result = $queued_message->move($self->{queue_dir}); + if ($result) + { + return $result; + } + + $self->{files}->{$queued_message->{id}} = $queued_message; + + return $result; +} + +## +## ADD_QUEUE - Adds another Queue's QueuedMessages to this Queue. +## +## Adds all of the QueuedMessage objects in the passed in queue +## to this queue. +## + +sub add_queue +{ + my $self = shift; + my $queue = shift; + my $id; + my $queued_message; + my $result; + + while (($id, $queued_message) = each %{$queue->{files}}) + { + $result = $self->add_queued_message($queued_message); + if ($result) + { + print("$result.\n"); + } + } +} + +## +## ADD - Adds an item to this queue. +## +## Adds either a Queue or a QueuedMessage to this Queue. +## + +sub add +{ + my $self = shift; + my $source = shift; + my $type_name; + my $result; + + $type_name = ref($source); + + if ($type_name eq "QueuedMessage") + { + return $self->add_queued_message($source); + } + + if ($type_name eq "Queue") + { + return $self->add_queue($source); + } + + return "Queue does not know how to add a '$type_name'" +} + +sub delete +{ + my $self = shift; + my $id; + my $queued_message; + + while (($id, $queued_message) = each %{$self->{files}}) + { + $result = $queued_message->delete(); + if ($result) + { + print("$result.\n"); + } + } +} + +sub bounce +{ + my $self = shift; + my $id; + my $queued_message; + + while (($id, $queued_message) = each %{$self->{files}}) + { + $result = $queued_message->bounce(); + if ($result) + { + print("$result.\n"); + } + } +} + +## +## Condition Class +## +## This next section is for any class that has an interface called +## check_move(source, dest). Each class represents some condition to +## check for to determine whether we should move the file from +## source to dest. +## + + +## +## OlderThan +## +## This Condition Class checks the modification time of the +## source file and returns true if the file's modification time is +## older than the number of seconds the class was initialzed with. +## + +package OlderThan; + +sub new +{ + my $this = shift; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->initialize(@_); + return $self; +} + +sub initialize +{ + my $self = shift; + + $self->{age_in_seconds} = shift; +} + +sub check_move +{ + my $self = shift; + my $source = shift; + + if ((time() - $source->last_modified_time()) > $self->{age_in_seconds}) + { + return 1; + } + + return 0; +} + +## +## Compound +## +## Takes a list of Move Condition Classes. Check_move returns true +## if every Condition Class in the list's check_move function returns +## true. +## + +package Compound; + +sub new +{ + my $this = shift; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->initialize(@_); + return $self; +} + +sub initialize +{ + my $self = shift; + + $self->{condition_list} = []; +} + +sub add +{ + my $self = shift; + my $new_condition = shift; + + push(@{$self->{condition_list}}, $new_condition); +} + +sub check_move +{ + my $self = shift; + my $source = shift; + my $dest = shift; + my $condition; + my $result; + + foreach $condition (@{$self->{condition_list}}) + { + if (!$condition->check_move($source, $dest)) + { + return 0; + } + } + + return 1; +} + +## +## Eval +## +## Takes a perl expression and evaluates it. The ControlFile object +## for the source QueuedMessage is avaliable through the name '$msg'. +## + +package Eval; + +sub new +{ + my $this = shift; + my $class = ref($this) || $this; + my $self = {}; + bless $self, $class; + $self->initialize(@_); + return $self; +} + +sub initialize +{ + my $self = shift; + + $self->{expression} = shift; +} + +sub check_move +{ + my $self = shift; + my $source = shift; + my $dest = shift; + my $result; + my %msg; + + $source->setup_vars(); + tie(%msg, 'QueuedMessage', $source); + $result = eval($self->{expression}); + + return $result; +} diff --git a/contrib/sendmail/contrib/re-mqueue.pl b/contrib/sendmail/contrib/re-mqueue.pl index 61aef43..9f8d819 100644 --- a/contrib/sendmail/contrib/re-mqueue.pl +++ b/contrib/sendmail/contrib/re-mqueue.pl @@ -84,9 +84,28 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# @(#)$Id: re-mqueue,v 1.3 1995/05/25 18:14:53 p-pomes Exp $ +# @(#)$OrigId: re-mqueue,v 1.3 1995/05/25 18:14:53 p-pomes Exp $ +# +# Updated by Graeme Hewson <ghewson@uk.oracle.com> May 1999 +# +# 'use Sys::Syslog' for Perl 5 +# Move transcript (xf) files if they exist +# Allow zero-length df files (empty message body) +# Preserve $! for error messages +# +# Updated by Graeme Hewson <ghewson@uk.oracle.com> April 2000 +# +# Improve handling of race between re-mqueue and sendmail +# +# Updated by Graeme Hewson <graeme.hewson@oracle.com> June 2000 +# +# Don't exit(0) at end so can be called as subroutine +# +# NB This program can't handle separate qf/df/xf subdirectories +# as introduced in sendmail 8.10.0. +# -require "syslog.pl"; +use Sys::Syslog; $LOCK_EX = 2; $LOCK_NB = 4; @@ -126,19 +145,19 @@ $now = time(); while ($dfile = pop(@dfiles)) { print "Checking $dfile\n" if ($debug); ($qfile = $dfile) =~ s/^d/q/; + ($xfile = $dfile) =~ s/^d/x/; ($mfile = $dfile) =~ s/^df//; - if (! -e $dfile || -z $dfile) { - print "$dfile is gone or zero bytes - skipping\n" if ($debug); - next; - } if (! -e $qfile || -z $qfile) { print "$qfile is gone or zero bytes - skipping\n" if ($debug); next; } - $mtime = $now; ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($dfile); + if (! defined $mtime) { + print "$dfile is gone - skipping\n" if ($debug); + next; + } # Compare timestamps if (($mtime + $age) > $now) { @@ -155,6 +174,10 @@ while ($dfile = pop(@dfiles)) { print "$queueb/$qfile already exists - skipping\n" if ($debug); next; } + if (-e "$queueB/$xfile") { + print "$queueb/$xfile already exists - skipping\n" if ($debug); + next; + } # Try and lock qf* file unless (open(QF, ">>$qfile")) { @@ -169,35 +192,67 @@ while ($dfile = pop(@dfiles)) { } print "$qfile now flock()ed\n" if ($debug); + # Check df* file again in case sendmail got in + if (! -e $dfile) { + print "$mfile sent - skipping\n" if ($debug); + # qf* file created by ourselves at open? (Almost certainly) + if (-z $qfile) { + unlink($qfile); + } + close(QF); + next; + } + # Show time! Do the link()s if (link("$dfile", "$queueB/$dfile") == 0) { - &syslog('err', 'link(%s, %s/%s): %m', $dfile, $queueB, $dfile); - print STDERR "$0: link($dfile, $queueB/$dfile): $!\n"; + $bang = $!; + &syslog('err', 'link(%s, %s/%s): %s', $dfile, $queueB, $dfile, $bang); + print STDERR "$0: link($dfile, $queueB/$dfile): $bang\n"; exit (1); } if (link("$qfile", "$queueB/$qfile") == 0) { - &syslog('err', 'link(%s, %s/%s): %m', $qfile, $queueB, $qfile); - print STDERR "$0: link($qfile, $queueB/$qfile): $!\n"; + $bang = $!; + &syslog('err', 'link(%s, %s/%s): %s', $qfile, $queueB, $qfile, $bang); + print STDERR "$0: link($qfile, $queueB/$qfile): $bang\n"; unlink("$queueB/$dfile"); exit (1); } + if (-e "$xfile") { + if (link("$xfile", "$queueB/$xfile") == 0) { + $bang = $!; + &syslog('err', 'link(%s, %s/%s): %s', $xfile, $queueB, $xfile, $bang); + print STDERR "$0: link($xfile, $queueB/$xfile): $bang\n"; + unlink("$queueB/$dfile"); + unlink("$queueB/$qfile"); + exit (1); + } + } # Links created successfully. Unlink the original files, release the # lock, and close the file. print "links ok\n" if ($debug); if (unlink($qfile) == 0) { - &syslog('err', 'unlink(%s): %m', $qfile); - print STDERR "$0: unlink($qfile): $!\n"; + $bang = $!; + &syslog('err', 'unlink(%s): %s', $qfile, $bang); + print STDERR "$0: unlink($qfile): $bang\n"; exit (1); } if (unlink($dfile) == 0) { - &syslog('err', 'unlink(%s): %m', $dfile); - print STDERR "$0: unlink($dfile): $!\n"; + $bang = $!; + &syslog('err', 'unlink(%s): %s', $dfile, $bang); + print STDERR "$0: unlink($dfile): $bang\n"; exit (1); } + if (-e "$xfile") { + if (unlink($xfile) == 0) { + $bang = $!; + &syslog('err', 'unlink(%s): %s', $xfile, $bang); + print STDERR "$0: unlink($xfile): $bang\n"; + exit (1); + } + } flock(QF, $LOCK_UN); close(QF); &syslog('info', '%s moved to %s', $mfile, $queueB); print "Done with $dfile $qfile\n\n" if ($debug); } -exit 0; diff --git a/contrib/sendmail/contrib/smcontrol.pl b/contrib/sendmail/contrib/smcontrol.pl index b5ef1f8..3ecfee1 100755 --- a/contrib/sendmail/contrib/smcontrol.pl +++ b/contrib/sendmail/contrib/smcontrol.pl @@ -21,7 +21,7 @@ sub get_controlname my $cn = undef; my $qd = undef; - open(CF, "</etc/sendmail.cf") or return $cn; + open(CF, "</etc/mail/sendmail.cf") or return $cn; while (<CF>) { chomp; @@ -162,7 +162,7 @@ sub munge_status my $cooked = ""; my $daemonStatus = ""; - if ($raw =~ /^(\d+)\/(\d+)$/mg) + if ($raw =~ /^(\d+)\/(\d+)\/(\d+)\/(\d+)/mg) { $cooked .= "Current number of children: $1"; if ($2 > 0) @@ -170,6 +170,8 @@ sub munge_status $cooked .= " (maximum $2)"; } $cooked .= "\n"; + $cooked .= "QueueDir free disk space (in blocks): $3\n"; + $cooked .= "Load average: $4\n"; } while ($raw =~ /^(\d+) (.*)$/mg) { |