summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1998-08-03 05:56:20 +0000
committerpeter <peter@FreeBSD.org>1998-08-03 05:56:20 +0000
commit329949050501501c130d09efc3aee7c78c6d4f9c (patch)
tree0772be9f4640bcba9db4a0de9e79a39ad377d80b /contrib/sendmail/src
downloadFreeBSD-src-329949050501501c130d09efc3aee7c78c6d4f9c.zip
FreeBSD-src-329949050501501c130d09efc3aee7c78c6d4f9c.tar.gz
Import sendmail-8.9.1 (slightly trimmed) onto a fresh branch under
src/contrib as per various discussions. I will copy across our changes and then point the Makefiles across once the dust has settled..
Diffstat (limited to 'contrib/sendmail/src')
-rwxr-xr-xcontrib/sendmail/src/Build513
-rw-r--r--contrib/sendmail/src/Makefile.m4149
-rw-r--r--contrib/sendmail/src/README1442
-rw-r--r--contrib/sendmail/src/TRACEFLAGS79
-rw-r--r--contrib/sendmail/src/alias.c894
-rw-r--r--contrib/sendmail/src/aliases53
-rw-r--r--contrib/sendmail/src/aliases.585
-rw-r--r--contrib/sendmail/src/arpadate.c197
-rw-r--r--contrib/sendmail/src/cdefs.h123
-rw-r--r--contrib/sendmail/src/clock.c267
-rw-r--r--contrib/sendmail/src/collect.c752
-rw-r--r--contrib/sendmail/src/conf.c4798
-rw-r--r--contrib/sendmail/src/conf.h2440
-rw-r--r--contrib/sendmail/src/convtime.c183
-rw-r--r--contrib/sendmail/src/daemon.c2073
-rw-r--r--contrib/sendmail/src/deliver.c3722
-rw-r--r--contrib/sendmail/src/domain.c913
-rw-r--r--contrib/sendmail/src/envelope.c938
-rw-r--r--contrib/sendmail/src/err.c767
-rw-r--r--contrib/sendmail/src/headers.c1570
-rw-r--r--contrib/sendmail/src/ldap_map.h71
-rw-r--r--contrib/sendmail/src/macro.c437
-rw-r--r--contrib/sendmail/src/mailq.167
-rw-r--r--contrib/sendmail/src/mailstats.h34
-rw-r--r--contrib/sendmail/src/main.c2701
-rwxr-xr-xcontrib/sendmail/src/makesendmail513
-rw-r--r--contrib/sendmail/src/map.c5065
-rw-r--r--contrib/sendmail/src/mci.c1293
-rw-r--r--contrib/sendmail/src/mime.c1171
-rw-r--r--contrib/sendmail/src/newaliases.147
-rw-r--r--contrib/sendmail/src/parseaddr.c2547
-rw-r--r--contrib/sendmail/src/pathnames.h32
-rw-r--r--contrib/sendmail/src/queue.c2402
-rw-r--r--contrib/sendmail/src/readcf.c2868
-rw-r--r--contrib/sendmail/src/recipient.c1431
-rw-r--r--contrib/sendmail/src/safefile.c751
-rw-r--r--contrib/sendmail/src/savemail.c1487
-rw-r--r--contrib/sendmail/src/sendmail.8577
-rw-r--r--contrib/sendmail/src/sendmail.h1500
-rw-r--r--contrib/sendmail/src/sendmail.hf119
-rw-r--r--contrib/sendmail/src/snprintf.c428
-rw-r--r--contrib/sendmail/src/srvrsmtp.c1532
-rw-r--r--contrib/sendmail/src/stab.c219
-rw-r--r--contrib/sendmail/src/stats.c135
-rw-r--r--contrib/sendmail/src/sysexits.c162
-rw-r--r--contrib/sendmail/src/trace.c111
-rw-r--r--contrib/sendmail/src/udb.c1264
-rw-r--r--contrib/sendmail/src/useful.h58
-rw-r--r--contrib/sendmail/src/usersmtp.c1166
-rw-r--r--contrib/sendmail/src/util.c2094
-rw-r--r--contrib/sendmail/src/version.c17
51 files changed, 54257 insertions, 0 deletions
diff --git a/contrib/sendmail/src/Build b/contrib/sendmail/src/Build
new file mode 100755
index 0000000..ab8a49d
--- /dev/null
+++ b/contrib/sendmail/src/Build
@@ -0,0 +1,513 @@
+#!/bin/sh
+
+# Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+# Copyright (c) 1993, 1996-1997 Eric P. Allman. All rights reserved.
+# Copyright (c) 1993
+# The Regents of the University of California. 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.
+#
+#
+# @(#)Build 8.93 (Berkeley) 6/24/98
+#
+
+#
+# A quick-and-dirty script to compile sendmail and related programs
+# in the presence of multiple architectures. To use, just use
+# "sh Build".
+#
+
+trap "rm -f $obj/.settings$$; exit" 1 2 3 15
+
+cflag=""
+mflag=""
+sflag=""
+makeargs=""
+libdirs=""
+incdirs=""
+libsrch=""
+siteconfig=""
+EX_USAGE=64
+EX_NOINPUT=66
+EX_UNAVAILABLE=69
+
+while [ ! -z "$1" ]
+do
+ case $1
+ in
+ -c) # clean out existing $obj tree
+ cflag=1
+ shift
+ ;;
+
+ -m) # show Makefile name only
+ mflag=1
+ shift
+ ;;
+
+ -E*) # environment variables to pass into Build
+ arg=`echo $1 | sed 's/^-E//'`
+ if [ -z "$arg" ]
+ then
+ shift # move to argument
+ arg=$1
+ fi
+ if [ -z "$arg" ]
+ then
+ echo "Empty -E flag" >&2
+ exit $EX_USAGE
+ else
+ case $arg
+ in
+ *=*) # check format
+ eval $arg
+ export `echo $arg | sed 's;=.*;;'`
+ ;;
+ *) # bad format
+ echo "Bad format for -E argument ($arg)" >&2
+ exit $EX_USAGE
+ ;;
+ esac
+ shift
+ fi
+ ;;
+
+ -L*) # set up LIBDIRS
+ libdirs="$libdirs $1"
+ shift
+ ;;
+
+ -I*) # set up INCDIRS
+ incdirs="$incdirs $1"
+ shift
+ ;;
+
+ -f*) # select site config file
+ arg=`echo $1 | sed 's/^-f//'`
+ if [ -z "$arg" ]
+ then
+ shift # move to argument
+ arg=$1
+ fi
+ if [ "$siteconfig" ]
+ then
+ echo "Only one -f flag allowed" >&2
+ exit $EX_USAGE
+ else
+ siteconfig=$arg
+ if [ -z "$siteconfig" ]
+ then
+ echo "Missing argument for -f flag" >&2
+ exit $EX_USAGE
+ elif [ ! -f "$siteconfig" ]
+ then
+ echo "${siteconfig}: File not found"
+ exit $EX_NOINPUT
+ else
+ shift # move past argument
+ fi
+ fi
+ ;;
+
+ -S) # skip auto-configure
+ sflag="-s"
+ shift
+ ;;
+
+ *) # pass argument to make
+ makeargs="$makeargs \"$1\""
+ shift
+ ;;
+ esac
+done
+
+#
+# Do heuristic guesses !ONLY! for machines that do not have uname
+#
+if [ -d /NextApps -a ! -f /bin/uname -a ! -f /usr/bin/uname ]
+then
+ # probably a NeXT box
+ arch=`hostinfo | sed -n 's/.*Processor type: \([^ ]*\).*/\1/p'`
+ os=NeXT
+ rel=`hostinfo | sed -n 's/.*NeXT Mach \([0-9\.]*\).*/\1/p'`
+elif [ -f /usr/sony/bin/machine -a -f /etc/osversion ]
+then
+ # probably a Sony NEWS 4.x
+ os=NEWS-OS
+ rel=`awk '{ print $3}' /etc/osversion`
+ arch=`/usr/sony/bin/machine`
+elif [ -d /usr/omron -a -f /bin/luna ]
+then
+ # probably a Omron LUNA
+ os=LUNA
+ if [ -f /bin/luna1 ] && /bin/luna1
+ then
+ rel=unios-b
+ arch=luna1
+ elif [ -f /bin/luna2 ] && /bin/luna2
+ then
+ rel=Mach
+ arch=luna2
+ elif [ -f /bin/luna88k ] && /bin/luna88k
+ then
+ rel=Mach
+ arch=luna88k
+ fi
+elif [ -d /usr/apollo -a -d \`node_data ]
+then
+ # probably a Apollo/DOMAIN
+ os=DomainOS
+ arch=$ISP
+ rel=`/usr/apollo/bin/bldt | grep Domain | awk '{ print $4 }' | sed -e 's/,//g'`
+fi
+
+if [ ! "$arch" -a ! "$os" -a ! "$rel" ]
+then
+ arch=`uname -m | sed -e 's/ //g'`
+ os=`uname -s | sed -e 's/\//-/g' -e 's/ //g'`
+ rel=`uname -r | sed -e 's/(/-/g' -e 's/)//g'`
+fi
+
+#
+# Tweak the values we have already got. PLEASE LIMIT THESE to
+# tweaks that are absolutely necessary because your system uname
+# routine doesn't return something sufficiently unique. Don't do
+# it just because you don't like the name that is returned. You
+# can combine the architecture name with the os name to create a
+# unique Makefile name.
+#
+
+# tweak machine architecture
+case $arch
+in
+ sun4*) arch=sun4;;
+
+ 9000/*) arch=`echo $arch | sed -e 's/9000.//' -e 's/..$/xx/'`;;
+
+ DS/907000) arch=ds90;;
+
+ NILE*) arch=NILE
+ os=`uname -v`;;
+esac
+
+# tweak operating system type and release
+node=`uname -n | sed -e 's/\//-/g' -e 's/ //g'`
+if [ "$os" = "$node" -a "$arch" = "i386" -a "$rel" = 3.2 -a "`uname -v`" = 2 ]
+then
+ # old versions of SCO UNIX set uname -s the same as uname -n
+ os=SCO_SV
+fi
+if [ "$rel" = 4.0 ]
+then
+ case $arch in
+ 3[34]??|3[34]??,*)
+ if [ -d /usr/sadm/sysadm/add-ons/WIN-TCP ]
+ then
+ os=NCR.MP-RAS.2.x
+ elif [ -d /usr/sadm/sysadm/add-ons/inet ]
+ then
+ os=NCR.MP-RAS.3.x
+ fi
+ ;;
+ esac
+fi
+
+case $os
+in
+ DYNIX-ptx) os=PTX;;
+ Paragon*) os=Paragon;;
+ HP-UX) rel=`echo $rel | sed -e 's/^[^.]*\.0*//'`;;
+ AIX) rela=$rel
+ rel=`uname -v`
+ case $rel in
+ 2) arch=""
+ ;;
+ 4) if [ "$rela" = "3" ]
+ then
+ arch=$rela
+ fi
+ ;;
+ esac
+ rel=$rel.$rela
+ ;;
+ BSD-386) os=BSD-OS;;
+ SCO_SV) os=SCO; rel=`uname -X | sed -n 's/Release = 3.2v//p'`;;
+ UNIX_System_V) if [ "$arch" = "ds90" ]
+ then
+ os="UXPDS"
+ rel=`uname -v | sed -e 's/\(V.*\)L.*/\1/'`
+ fi;;
+ SINIX-?) os=SINIX;;
+ DomainOS) case $rel in
+ 10.4*) rel=10.4;;
+ esac
+ ;;
+esac
+
+# get "base part" of operating system release
+rroot=`echo $rel | sed -e 's/\.[^.]*$//'`
+rbase=`echo $rel | sed -e 's/\..*//'`
+if [ "$rroot" = "$rbase" ]
+then
+ rroot=$rel
+fi
+
+# heuristic tweaks to clean up names -- PLEASE LIMIT THESE!
+if [ "$os" = "unix" ]
+then
+ # might be Altos System V
+ case $rel
+ in
+ 5.3*) os=Altos;;
+ esac
+elif [ -r /unix -a -r /usr/lib/libseq.a -a -r /lib/cpp ]
+then
+ # might be a DYNIX/ptx 2.x system, which has a broken uname
+ if strings /lib/cpp | grep _SEQUENT_ > /dev/null
+ then
+ os=PTX
+ fi
+elif [ -d /usr/nec ]
+then
+ # NEC machine -- what is it running?
+ if [ "$os" = "UNIX_System_V" ]
+ then
+ os=EWS-UX_V
+ elif [ "$os" = "UNIX_SV" ]
+ then
+ os=UX4800
+ fi
+elif [ "$arch" = "mips" ]
+then
+ case $rel
+ in
+ 4_*)
+ if [ `uname -v` = "UMIPS" ]
+ then
+ os=RISCos
+ fi;;
+ esac
+fi
+
+# see if there is a "user suffix" specified
+if [ "${SENDMAIL_SUFFIX-}x" = "x" ]
+then
+ sfx=""
+else
+ sfx=".${SENDMAIL_SUFFIX}"
+fi
+
+echo "Configuration: os=$os, rel=$rel, rbase=$rbase, rroot=$rroot, arch=$arch, sfx=$sfx"
+
+
+SMROOT=${SMROOT-..}
+BUILDTOOLS=${BUILDTOOLS-$SMROOT/BuildTools}
+export SMROOT BUILDTOOLS
+
+# see if we are in a Build-able directory
+if [ ! -f Makefile.m4 ]; then
+ echo "Makefile.m4 not found. Build can only be run from a source directory."
+ exit $EX_UNAVAILABLE
+fi
+
+# now try to find a reasonable object directory
+if [ -r obj.$os.$rel.$arch$sfx ]; then
+ obj=obj.$os.$rel.$arch$sfx
+elif [ -r obj.$os.$rroot.$arch$sfx ]; then
+ obj=obj.$os.$rroot.$arch$sfx
+elif [ -r obj.$os.$rbase.x.$arch$sfx ]; then
+ obj=obj.$os.$rbase.x.$arch$sfx
+elif [ -r obj.$os.$rel$sfx ]; then
+ obj=obj.$os.$rel$sfx
+elif [ -r obj.$os.$rbase.x$sfx ]; then
+ obj=obj.$os.$rbase.x$sfx
+elif [ -r obj.$os.$arch$sfx ]; then
+ obj=obj.$os.$arch$sfx
+elif [ -r obj.$rel.$arch$sfx ]; then
+ obj=obj.$rel.$arch$sfx
+elif [ -r obj.$rbase.x.$arch$sfx ]; then
+ obj=obj.$rbase.x.$arch$sfx
+elif [ -r obj.$os$sfx ]; then
+ obj=obj.$os$sfx
+elif [ -r obj.$arch$sfx ]; then
+ obj=obj.$arch$sfx
+elif [ -r obj.$rel$sfx ]; then
+ obj=obj.$rel$sfx
+elif [ -r obj$sfx ]; then
+ obj=obj$sfx
+fi
+if [ -z "$obj" -o "$cflag" ]
+then
+ if [ -n "$obj" ]
+ then
+ echo "Clearing out existing $obj tree"
+ rm -rf $obj
+ else
+ # no existing obj directory -- try to create one if Makefile found
+ obj=obj.$os.$rel.$arch$sfx
+ fi
+ if [ -r $BUILDTOOLS/OS/$os.$rel.$arch$sfx ]; then
+ oscf=$os.$rel.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rel.$arch ]; then
+ oscf=$os.$rel.$arch
+ elif [ -r $BUILDTOOLS/OS/$os.$rroot.$arch$sfx ]; then
+ oscf=$os.$rroot.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rroot.$arch ]; then
+ oscf=$os.$rroot.$arch
+ elif [ -r $BUILDTOOLS/OS/$os.$rbase.x.$arch$sfx ]; then
+ oscf=$os.$rbase.x.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rbase.x.$arch ]; then
+ oscf=$os.$rbase.x.$arch
+ elif [ -r $BUILDTOOLS/OS/$os.$rel$sfx ]; then
+ oscf=$os.$rel$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rel ]; then
+ oscf=$os.$rel
+ elif [ -r $BUILDTOOLS/OS/$os.$rroot$sfx ]; then
+ oscf=$os.$rroot$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rroot ]; then
+ oscf=$os.$rroot
+ elif [ -r $BUILDTOOLS/OS/$os.$rbase.x$sfx ]; then
+ oscf=$os.$rbase.x$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rbase.x ]; then
+ oscf=$os.$rbase.x
+ elif [ -r $BUILDTOOLS/OS/$os.$arch$sfx ]; then
+ oscf=$os.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$arch ]; then
+ oscf=$os.$arch
+ elif [ -r $BUILDTOOLS/OS/$rel.$arch$sfx ]; then
+ oscf=$rel.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$rel.$arch ]; then
+ oscf=$rel.$arch
+ elif [ -r $BUILDTOOLS/OS/$rroot.$arch$sfx ]; then
+ oscf=$rroot.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$rroot.$arch ]; then
+ oscf=$rroot.$arch
+ elif [ -r $BUILDTOOLS/OS/$rbase.x.$arch$sfx ]; then
+ oscf=$rbase.x.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$rbase.x.$arch ]; then
+ oscf=$rbase.x.$arch
+ elif [ -r $BUILDTOOLS/OS/$os$sfx ]; then
+ oscf=$os$sfx
+ elif [ -r $BUILDTOOLS/OS/$os ]; then
+ oscf=$os
+ elif [ -r $BUILDTOOLS/OS/$arch$sfx ]; then
+ oscf=$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$arch ]; then
+ oscf=$arch
+ elif [ -r $BUILDTOOLS/OS/$rel$sfx ]; then
+ oscf=$rel$sfx
+ elif [ -r $BUILDTOOLS/OS/$rel ]; then
+ oscf=$rel
+ elif [ -r $BUILDTOOLS/OS/$rel$sfx ]; then
+ oscf=$rel$sfx
+ else
+ echo "Cannot determine how to support $arch.$os.$rel" >&2
+ exit $EX_UNAVAILABLE
+ fi
+ M4=`sh $BUILDTOOLS/bin/find_m4.sh`
+ ret=$?
+ if [ $ret -ne 0 ]
+ then
+ exit $ret
+ fi
+ echo "Using M4=$M4"
+ export M4
+ if [ "$mflag" ]
+ then
+ echo "Will run in virgin $obj using $BUILDTOOLS/OS/$oscf"
+ exit 0
+ fi
+ if [ "$ABI" ]
+ then
+ echo "Using ABI $ABI"
+ fi
+ echo "Creating $obj using $BUILDTOOLS/OS/$oscf"
+ mkdir $obj
+ (cd $obj; ln -s ../*.[ch158] .)
+ if [ -f sendmail.hf ]
+ then
+ (cd $obj; ln -s ../sendmail.hf .)
+ fi
+
+ rm -f $obj/.settings$$
+ echo 'divert(-1)' > $obj/.settings$$
+ cat $BUILDTOOLS/M4/header.m4 >> $obj/.settings$$
+ if [ "$ABI" ]
+ then
+ echo "define(\`confABI', \`$ABI')" >> $obj/.settings$$
+ fi
+ cat $BUILDTOOLS/OS/$oscf >> $obj/.settings$$
+
+ if [ -z "$siteconfig" ]
+ then
+ # none specified, use defaults
+ if [ -f $BUILDTOOLS/Site/site.$oscf$sfx.m4 ]
+ then
+ siteconfig=$BUILDTOOLS/Site/site.$oscf$sfx.m4
+ elif [ -f $BUILDTOOLS/Site/site.$oscf.m4 ]
+ then
+ siteconfig=$BUILDTOOLS/Site/site.$oscf.m4
+ fi
+ if [ -f $BUILDTOOLS/Site/site.config.m4 ]
+ then
+ siteconfig="$BUILDTOOLS/Site/site.config.m4 $siteconfig"
+ fi
+ fi
+ if [ ! -z "$siteconfig" ]
+ then
+ echo "Including $siteconfig"
+ cat $siteconfig >> $obj/.settings$$
+ fi
+ if [ "$libdirs" ]
+ then
+ echo "define(\`confLIBDIRS', confLIBDIRS \`\`$libdirs'')" >> $obj/.settings$$
+ fi
+ if [ "$incdirs" ]
+ then
+ echo "define(\`confINCDIRS', confINCDIRS \`\`$incdirs'')" >> $obj/.settings$$
+ fi
+ echo 'divert(0)dnl' >> $obj/.settings$$
+ libdirs=`(cat $obj/.settings$$; echo "_SRIDBIL_= confLIBDIRS" ) | \
+ sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' | \
+ ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - | \
+ grep "^_SRIDBIL_=" | \
+ sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' -e "s/^_SRIDBIL_=//"`
+ libsrch=`(cat $obj/.settings$$; echo "_HCRSBIL_= confLIBSEARCH" ) | \
+ sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' | \
+ ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - | \
+ grep "^_HCRSBIL_=" | \
+ sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' -e "s/^_HCRSBIL_=//"`
+ echo 'divert(-1)' >> $obj/.settings$$
+ LIBDIRS="$libdirs" LIBSRCH="$libsrch" SITECONFIG="$siteconfig" sh $BUILDTOOLS/bin/configure.sh $sflag $oscf >> $obj/.settings$$
+ echo 'divert(0)dnl' >> $obj/.settings$$
+ sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' $obj/.settings$$ | \
+ ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - Makefile.m4 | \
+ sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' > $obj/Makefile
+ if [ $? -ne 0 -o ! -s $obj/Makefile ]
+ then
+ echo "ERROR: ${M4} failed; You may need a newer version of M4, at least as new as System V or GNU" 1>&2
+ rm -rf $obj
+ exit $EX_UNAVAILABLE
+ fi
+ rm -f $obj/.settings$$
+ echo "Making dependencies in $obj"
+ (cd $obj; ${MAKE-make} depend)
+fi
+
+if [ "$mflag" ]
+then
+ makefile=`ls -l $obj/Makefile | sed 's/.* //'`
+ if [ -z "$makefile" ]
+ then
+ echo "ERROR: $obj exists but has no Makefile" >&2
+ exit $EX_NOINPUT
+ fi
+ echo "Will run in existing $obj using $makefile"
+ exit 0
+fi
+
+echo "Making in $obj"
+cd $obj
+eval exec ${MAKE-make} $makeargs
diff --git a/contrib/sendmail/src/Makefile.m4 b/contrib/sendmail/src/Makefile.m4
new file mode 100644
index 0000000..f4229c2
--- /dev/null
+++ b/contrib/sendmail/src/Makefile.m4
@@ -0,0 +1,149 @@
+#
+# This Makefile is designed to work on any reasonably current version of
+# "make" program.
+#
+# @(#)Makefile.m4 8.23 (Berkeley) 6/16/98
+#
+
+# C compiler
+CC= confCC
+
+# Shell
+SHELL= confSHELL
+
+# use O=-O (usual) or O=-g (debugging)
+O= ifdef(`confOPTIMIZE', `confOPTIMIZE', `-O')
+
+# location of sendmail source directory
+SRCDIR= .
+
+# define the database mechanisms available for map & alias lookups:
+# -DNDBM -- use new DBM
+# -DNEWDB -- use new Berkeley DB
+# -DNIS -- include NIS support
+# The really old (V7) DBM library is no longer supported.
+# See README for a description of how these flags interact.
+#
+MAPDEF= ifdef(`confMAPDEF', `confMAPDEF')
+
+# environment definitions (e.g., -D_AIX3)
+ENVDEF= ifdef(`confENVDEF', `confENVDEF')
+
+# see also conf.h for additional compilation flags
+
+# include directories
+INCDIRS=confINCDIRS
+
+# loader options
+LDOPTS= ifdef(`confLDOPTS', `confLDOPTS')
+
+# library directories
+LIBDIRS=confLIBDIRS
+
+# libraries required on your system
+# delete -l44bsd if you are not running BIND 4.9.x
+LIBS= ifdef(`confLIBS', `confLIBS')
+
+# location of sendmail binary (usually /usr/sbin or /usr/lib)
+BINDIR= ${DESTDIR}ifdef(`confMBINDIR', `confMBINDIR', `/usr/sbin')
+
+# location of "user" binaries (usually /usr/bin or /usr/ucb)
+UBINDIR=${DESTDIR}ifdef(`confUBINDIR', `confUBINDIR', `/usr/bin')
+
+# location of sendmail.st file (usually /var/log or /usr/lib)
+STDIR= ${DESTDIR}ifdef(`confSTDIR', `confSTDIR', `/var/log')
+
+# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
+HFDIR= ${DESTDIR}ifdef(`confHFDIR', `confHFDIR', `/usr/share/misc')
+
+# additional .o files needed
+OBJADD= ifdef(`confOBJADD', `confOBJADD') ifdef(`confSMOBJADD', `confSMOBJADD')
+
+undivert(1)
+
+################### end of user configuration flags ######################
+
+BUILDBIN=confBUILDBIN
+COPTS= -I. ${INCDIRS} ${MAPDEF} ${ENVDEF}
+CFLAGS= $O ${COPTS}
+
+BEFORE= confBEFORE
+OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
+ deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
+ map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
+ safefile.o savemail.o snprintf.o srvrsmtp.o stab.o stats.o \
+ sysexits.o trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
+
+LINKS= ifdef(`confLINKS', `confLINKS',
+ `${UBINDIR}/newaliases \
+ ${UBINDIR}/mailq \
+ ${UBINDIR}/hoststat \
+ ${UBINDIR}/purgestat')
+
+NROFF= ifdef(`confNROFF', `confNROFF', `groff -Tascii')
+MANDOC= ifdef(`confMANDOC', `confMANDOC', `-mandoc')
+
+INSTALL=ifdef(`confINSTALL', `confINSTALL', `install')
+BINOWN= ifdef(`confSBINOWN', `confSBINOWN', `root')
+BINGRP= ifdef(`confSBINGRP', `confSBINGRP', `kmem')
+BINMODE=ifdef(`confSBINMODE', `confSBINMODE', `4555')
+
+MANOWN= ifdef(`confMANOWN', `confMANOWN', `bin')
+MANGRP= ifdef(`confMANGRP', `confMANGRP', `bin')
+MANMODE=ifdef(`confMANMODE', `confMANMODE', `444')
+
+MANROOT=${DESTDIR}ifdef(`confMANROOT', `confMANROOT', `/usr/share/man/cat')
+MAN1= ${MANROOT}ifdef(`confMAN1', `confMAN1', `1')
+MAN1EXT=ifdef(`confMAN1EXT', `confMAN1EXT', `1')
+MAN1SRC=ifdef(`confMAN1SRC', `confMAN1SRC', `0')
+MAN5= ${MANROOT}ifdef(`confMAN5', `confMAN5', `5')
+MAN5EXT=ifdef(`confMAN5EXT', `confMAN5EXT', `5')
+MAN5SRC=ifdef(`confMAN5SRC', `confMAN5SRC', `0')
+MAN8= ${MANROOT}ifdef(`confMAN8', `confMAN8', `8')
+MAN8EXT=ifdef(`confMAN8EXT', `confMAN8EXT', `8')
+MAN8SRC=ifdef(`confMAN8SRC', `confMAN8SRC', `0')
+
+ALL= sendmail aliases.${MAN5SRC} mailq.${MAN1SRC} newaliases.${MAN1SRC} sendmail.${MAN8SRC}
+
+all: ${ALL}
+
+sendmail: ${BEFORE} ${OBJS}
+ ${CC} -o sendmail ${LDOPTS} ${LIBDIRS} ${OBJS} ${LIBS}
+ cp /dev/null sendmail.st
+
+undivert(3)
+
+aliases.${MAN5SRC}: aliases.5
+ ${NROFF} ${MANDOC} aliases.5 > aliases.${MAN5SRC}
+
+mailq.${MAN1SRC}: mailq.1
+ ${NROFF} ${MANDOC} mailq.1 > mailq.${MAN1SRC}
+
+newaliases.${MAN1SRC}: newaliases.1
+ ${NROFF} ${MANDOC} newaliases.1 > newaliases.${MAN1SRC}
+
+sendmail.${MAN8SRC}: sendmail.8
+ ${NROFF} ${MANDOC} sendmail.8 > sendmail.${MAN8SRC}
+
+install: install-sendmail install-docs
+
+install-sendmail: sendmail
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
+ for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 sendmail.st \
+ ${STDIR}/sendmail.st
+
+install-docs: aliases.${MAN5SRC} mailq.${MAN1SRC} newaliases.${MAN1SRC} sendmail.${MAN8SRC}
+ifdef(`confNO_MAN_INSTALL', `dnl',
+` ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} sendmail.${MAN8SRC} ${MAN8}/sendmail.${MAN8EXT}
+ ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} aliases.${MAN5SRC} ${MAN5}/aliases.${MAN5EXT}
+ ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} mailq.${MAN1SRC} ${MAN1}/mailq.${MAN1EXT}
+ ${INSTALL} -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} newaliases.${MAN1SRC} ${MAN1}/newaliases.${MAN1EXT}')
+
+clean:
+ rm -f ${OBJS} sendmail aliases.${MAN5SRC} mailq.${MAN1SRC} newaliases.${MAN1SRC} sendmail.${MAN8SRC}
+
+################ Dependency scripts
+include(confBUILDTOOLSDIR/M4/depend/ifdef(`confDEPEND_TYPE', `confDEPEND_TYPE', `generic').m4)dnl
+################ End of dependency scripts
diff --git a/contrib/sendmail/src/README b/contrib/sendmail/src/README
new file mode 100644
index 0000000..a6f7dcf
--- /dev/null
+++ b/contrib/sendmail/src/README
@@ -0,0 +1,1442 @@
+# Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+# Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+# Copyright (c) 1988
+# The Regents of the University of California. 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.
+#
+#
+# @(#)README 8.206 (Berkeley) 6/30/98
+#
+
+This directory contains the source files for sendmail(TM).
+
+*********************
+!! DO NOT USE MAKE !! in this directory to compile sendmail --
+********************* instead, use the "Build" script located in
+the src directory. It will build an appropriate Makefile, and
+create an appropriate obj.* subdirectory so that multiplatform
+support works easily.
+
+ **********************************************************
+ ** Read below for more details on building sendmail. **
+ **********************************************************
+
+**************************************************************************
+** IMPORTANT: Read the appropriate paragraphs in the section on **
+** ``Operating System and Compile Quirks''. **
+**************************************************************************
+
+For detailed instructions, please read the document ../doc/op/op.me:
+
+ eqn ../doc/op/op.me | pic | ditroff -me
+
+Sendmail is a trademark of Sendmail, Inc.
+
+
++-------------------+
+| BUILDING SENDMAIL |
++-------------------+
+
+By far, the easiest way to compile sendmail is to use the "Build"
+script:
+
+ sh Build
+
+This uses the "uname" command to figure out what architecture you are
+on and creates a proper Makefile accordingly. It also creates a
+subdirectory per object format, so that multiarchitecture support is
+easy. In general this should be all you need. IRIX 6.x users should
+read the note below in the OPERATING SYSTEM AND COMPILE QUIRKS section.
+
+If you need to look at other include or library directories, use the
+-I or -L flags on the command line, e.g.,
+
+ sh Build -I/usr/sww/include -L/usr/sww/lib
+
+It's also possible to create local site configuration in the file
+site.config.m4 (or another file settable with the -f flag). This
+file contains M4 definitions for various compilation values; the
+most useful are:
+
+confMAPDEF -D flags to specify database types to be included
+ (see below)
+confENVDEF -D flags to specify other environment information
+confINCDIRS -I flags for finding include files during compilation
+confLIBDIRS -L flags for finding libraries during linking
+confLIBS -l flags for selecting libraries during linking
+confLDOPTS other ld(1) linker options
+
+Others can be found by examining Makefile.m4. Please read
+../BuildTools/README for more information about the site.config.m4
+file.
+
+You can recompile from scratch using the -c flag with the Build
+command. This removes the existing compilation directory for the
+current platform and builds a new one.
+
+Porting to a new Unix-based system should be a matter of creating
+an appropriate configuration file in the BuildTools/OS/ directory.
+
+
+
++----------------------+
+| DATABASE DEFINITIONS |
++----------------------+
+
+There are several database formats that can be used for the alias files
+and for general maps. When used for alias files they interact in an
+attempt to be backward compatible.
+
+The options are:
+
+NEWDB The new Berkeley DB package. Some systems (e.g., BSD/OS and
+ Digital UNIX 4.0) have some version of this package
+ pre-installed. If your system does not have Berkeley DB
+ pre-installed, or the version installed is not version 2.0
+ or greater (e.g., is Berkeley DB 1.85 or 1.86), get the
+ current version from http://www.sleepycat.com/. DO NOT
+ use a version from any of the University of California,
+ Berkeley "Net" or other distributions. If you are still
+ running BSD/386 1.x, you will need to upgrade the included
+ Berkeley DB library to a current version. NEWDB is included
+ automatically if the Build script can find a library named
+ libdb.a.
+NDBM The older NDBM implementation -- the very old V7 DBM
+ implementation is no longer supported.
+NIS Network Information Services. To use this you must have
+ NIS support on your system.
+NISPLUS NIS+ (the revised NIS released with Solaris 2). You must
+ have NIS+ support on your system to use this flag.
+HESIOD Support for Hesiod (from the DEC/Athena distribution). You
+ must already have Hesiod support on your system for this to
+ work. You may be able to get this to work with the MIT/Athena
+ version of Hesiod, but that's likely to be a lot of work.
+LDAPMAP Lightweight Directory Lookup Protocol support. You will
+ have to install the UMich ldap and lber libraries to use
+ this flag.
+MAP_REGEX Regular Expression support. You will need to use an
+ operating system which comes with the POSIX regex()
+ routines or install a regexp library such as libregex from
+ the Free Software Foundation.
+
+>>> NOTE WELL for NEWDB support: If you want to get ndbm support, for
+>>> Berkeley DB versions under 2.0, it is CRITICAL that you remove
+>>> ndbm.o from libdb.a before you install it and DO NOT install ndbm.h;
+>>> for Berkeley DB versions 2.0 through 2.3.14, remove dbm.o from libdb.a
+>>> before you install it. If you don't delete these, there is absolutely
+>>> no point to including -DNDBM, since it will just get you another
+>>> (inferior) API to the same format database. These files OVERRIDE
+>>> calls to ndbm routines -- in particular, if you leave ndbm.h in,
+>>> you can find yourself using the new db package even if you don't
+>>> define NEWDB. Berkeley DB versions later than 2.3.14 do not need
+>>> to be modified. Please also consult the README in the top level
+>>> directory of the sendmail distribution for other important information.
+>>>
+>>> Further note: DO NOT remove your existing /usr/include/ndbm.h --
+>>> you need that one. But do not install an updated ndbm.h in
+>>> /usr/include, /usr/local/include, or anywhere else.
+
+If NEWDB and NDBM are defined (but not NIS), then sendmail will read
+NDBM format alias files, but the next time a newaliases is run the
+format will be converted to NEWDB; that format will be used forever
+more. This is intended as a transition feature.
+
+If NEWDB, NDBM, and NIS are all defined and the name of the file includes
+the string "/yp/", sendmail will rebuild BOTH the NEWDB and NDBM format
+alias files. However, it will only read the NEWDB file; the NDBM format
+file is used only by the NIS subsystem. This is needed because the NIS
+maps on an NIS server are built directly from the NDBM files.
+
+If NDBM and NIS are defined (regardless of the definition of NEWDB),
+and the filename includes the string "/yp/", sendmail adds the special
+tokens "YP_LAST_MODIFIED" and "YP_MASTER_NAME", both of which are
+required if the NDBM file is to be used as an NIS map.
+
+All of these flags are normally defined in the DBMDEF line in the
+Makefile.
+
+If you define NEWDB or HESIOD you get the User Database (USERDB)
+automatically. Generally you do want to have NEWDB for it to do
+anything interesting. See above for getting the Berkeley DB
+package (i.e., NEWDB). There is no separate "user database"
+package -- don't bother searching for it on the net.
+
+Hesiod and LDAP require libraries that may not be installed with your
+system. These are outside of my ability to provide support. See the
+"Quirks" section for more information.
+
+The regex map can be used to see if an address matches a certain regular
+expression. For example, all-numerics local parts are common spam
+addresses, so "^[0-9]+$" would match this. By using such a map in a
+check_* rule-set, you can block a certain range of addresses that would
+otherwise be considered valid.
+
++---------------+
+| COMPILE FLAGS |
++---------------+
+
+Wherever possible, I try to make sendmail pull in the correct
+compilation options needed to compile on various environments based on
+automatically defined symbols. Some machines don't seem to have useful
+symbols available, requiring that a compilation flag be defined in
+the Makefile; see the Buildtools/OS subdirectory for the supported
+architectures.
+
+If you are a system to which sendmail has already been ported you
+should not have to touch the following symbols. But if you are porting,
+you may have to tweak the following compilation flags in conf.h in order
+to get it to compile and link properly:
+
+SYSTEM5 Adjust for System V (not necessarily Release 4).
+SYS5SIGNALS Use System V signal semantics -- the signal handler
+ is automatically dropped when the signal is caught.
+ If this is not set, use POSIX/BSD semantics, where the
+ signal handler stays in force until an exec or an
+ explicit delete. Implied by SYSTEM5.
+SYS5SETPGRP Use System V setpgrp() semantics. Implied by SYSTEM5.
+HASFCHMOD Define this to one if you have the fchmod(2) system call.
+ This improves security.
+HASFLOCK Set this if you prefer to use the flock(2) system call
+ rather than using fcntl-based locking. Fcntl locking
+ has some semantic gotchas, but many vendor systems
+ also interface it to lockd(8) to do NFS-style locking.
+ Unfortunately, may vendors implementations of fcntl locking
+ is just plain broken (e.g., locks are never released,
+ causing your sendmail to deadlock; when the kernel runs
+ out of locks your system crashes). For this reason, I
+ recommend always defining this unless you are absolutely
+ certain that your fcntl locking implementation really works.
+HASUNAME Set if you have the "uname" system call. Implied by
+ SYSTEM5.
+HASUNSETENV Define this if your system library has the "unsetenv"
+ subroutine.
+HASSETSID Define this if you have the setsid(2) system call. This
+ is implied if your system appears to be POSIX compliant.
+HASINITGROUPS Define this if you have the initgroups(3) routine.
+HASSETVBUF Define this if you have the setvbuf(3) library call.
+ If you don't, setlinebuf will be used instead. This
+ defaults on if your compiler defines __STDC__.
+HASSETREUID Define this if you have setreuid(2) ***AND*** root can
+ use setreuid to change to an arbitrary user. This second
+ condition is not satisfied on AIX 3.x. You may find that
+ your system has setresuid(2), (for example, on HP-UX) in
+ which case you will also have to #define setreuid(r, e)
+ to be the appropriate call. Some systems (such as Solaris)
+ have a compatibility routine that doesn't work properly,
+ but may have "saved user ids" properly implemented so you
+ can ``#define setreuid(r, e) seteuid(e)'' and have it work.
+ The important thing is that you have a call that will set
+ the effective uid independently of the real or saved uid
+ and be able to set the effective uid back again when done.
+ There's a test program in ../test/t_setreuid.c that will
+ try things on your system. Setting this improves the
+ security, since sendmail doesn't have to read .forward
+ and :include: files as root. There are certain attacks
+ that may be unpreventable without this call.
+USESETEUID Define this to 1 if you have a seteuid(2) system call that
+ will allow root to set only the effective user id to an
+ arbitrary value ***AND*** you have saved user ids. This is
+ preferable to HASSETREUID if these conditions are fulfilled.
+ These are the semantics of the to-be-released revision of
+ Posix.1. The test program ../test/t_seteuid.c will try
+ this out on your system. If you define both HASSETREUID
+ and USESETEUID, the former is ignored.
+HASLSTAT Define this if you have symbolic links (and thus the
+ lstat(2) system call). This improves security. Unlike
+ most other options, this one is on by default, so you
+ need to #undef it in conf.h if you don't have symbolic
+ links (these days everyone does).
+HASSETRLIMIT Define this to 1 if you have the setrlimit(2) syscall.
+ You can define it to 0 to force it off. It is assumed
+ if you are running a BSD-like system.
+HASULIMIT Define this if you have the ulimit(2) syscall (System V
+ style systems). HASSETRLIMIT overrides, as it is more
+ general.
+HASWAITPID Define this if you have the waitpid(2) syscall.
+HASGETDTABLESIZE
+ Define this if you have the getdtablesize(2) syscall.
+HAS_ST_GEN Define this to 1 if your system has the st_gen field in
+ the stat structure (see stat(2)).
+USESTRERROR Define this if you have the libc strerror function (which
+ should be declared in <errno.h>), and it should be used
+ instead of sys_errlist.
+NEEDGETOPT Define this if you need a reimplementation of getopt(3).
+ On some systems, getopt does very odd things if called
+ to scan the arguments twice. This flag will ask sendmail
+ to compile in a local version of getopt that works
+ properly.
+NEEDSTRTOL Define this if your standard C library does not define
+ strtol(3). This will compile in a local version.
+NEEDVPRINTF Define this if your standard C library does not define
+ vprintf(3). Note that the resulting fake implementation
+ is not very elegant and may not even work on some
+ architectures.
+NEEDFSYNC Define this if your standard C library does not define
+ fsync(2). This will try to simulate the operation using
+ fcntl(2); if that is not available it does nothing, which
+ isn't great, but at least it compiles and runs.
+HASGETUSERSHELL Define this to 1 if you have getusershell(3) in your
+ standard C library. If this is not defined, or is defined
+ to be 0, sendmail will scan the /etc/shells file (no
+ NIS-style support, defaults to /bin/sh and /bin/csh if
+ that file does not exist) to get a list of unrestricted
+ user shells. This is used to determine whether users
+ are allowed to forward their mail to a program or a file.
+NEEDPUTENV Define this if your system needs am emulation of the
+ putenv(3) call. Define to 1 to implement it in terms
+ of setenv(3) or to 2 to do it in terms of primitives.
+NOFTRUNCATE Define this if you don't have the ftruncate(2) syscall.
+ If you don't have this system call, there is an unavoidable
+ race condition that occurs when creating alias databases.
+GIDSET_T The type of entries in a gidset passed as the second
+ argument to getgroups(2). Historically this has been an
+ int, so this is the default, but some systems (such as
+ IRIX) pass it as a gid_t, which is an unsigned short.
+ This will make a difference, so it is important to get
+ this right! However, it is only an issue if you have
+ group sets.
+SLEEP_T The type returned by the system sleep() function.
+ Defaults to "unsigned int". Don't worry about this
+ if you don't have compilation problems.
+ARBPTR_T The type of an arbitrary pointer -- defaults to "void *".
+ If you are an very old compiler you may need to define
+ this to be "char *".
+SOCKADDR_LEN_T The type used for the third parameter to accept(2),
+ getsockname(2), and getpeername(2), representing the
+ length of a struct sockaddr. Defaults to int.
+SOCKOPT_LEN_T The type used for the fifth parameter to getsockopt(2)
+ and setsockopt(2), representing the length of the option
+ buffer. Defaults to int.
+LA_TYPE The type of load average your kernel supports. These
+ can be one of:
+ LA_ZERO (1) -- it always returns the load average as
+ "zero" (and does so on all architectures).
+ LA_INT (2) to read /dev/kmem for the symbol avenrun and
+ interpret as a long integer.
+ LA_FLOAT (3) same, but interpret the result as a floating
+ point number.
+ LA_SHORT (6) to interpret as a short integer.
+ LA_SUBR (4) if you have the getloadavg(3) routine in your
+ system library.
+ LA_MACH (5) to use MACH-style load averages (calls
+ processor_set_info()),
+ LA_PROCSTR (7) to read /proc/loadavg and interpret it
+ as a string representing a floating-point
+ number (Linux-style).
+ LA_READKSYM (8) is an implementation suitable for some
+ versions of SVr4 that uses the MIOC_READKSYM ioctl
+ call to read /dev/kmem.
+ LA_DGUX (9) is a special implementation for DG/UX that uses
+ the dg_sys_info system call.
+ LA_HPUX (10) is an HP-UX specific version that uses the
+ pstat_getdynamic system call.
+ LA_IRIX6 (11) is an IRIX 6.x specific version that adapts
+ to 32 or 64 bit kernels; it is otherwise very similar
+ to LA_INT.
+ LA_KSTAT (12) uses the (Solaris-specific) kstat(3k)
+ implementation.
+ LA_DEVSHORT (13) reads a short from a system file (default:
+ /dev/table/avenrun) and scales it in the same manner
+ as LA_SHORT.
+ LA_INT, LA_SHORT, LA_FLOAT, and LA_READKSYM have several
+ other parameters that they try to divine: the name of your
+ kernel, the name of the variable in the kernel to examine,
+ the number of bits of precision in a fixed point load average,
+ and so forth. LA_DEVSHORT uses _PATH_AVENRUN to find the
+ device to be read to find the load average.
+ In desperation, use LA_ZERO. The actual code is in
+ conf.c -- it can be tweaked if you are brave.
+FSHIFT For LA_INT, LA_SHORT, and LA_READKSYM, this is the number
+ of bits of load average after the binary point -- i.e.,
+ the number of bits to shift right in order to scale the
+ integer to get the true integer load average. Defaults to 8.
+_PATH_UNIX The path to your kernel. Needed only for LA_INT, LA_SHORT,
+ and LA_FLOAT. Defaults to "/unix" on System V, "/vmunix"
+ everywhere else.
+LA_AVENRUN For LA_INT, LA_SHORT, and LA_FLOAT, the name of the kernel
+ variable that holds the load average. Defaults to "avenrun"
+ on System V, "_avenrun" everywhere else.
+SFS_TYPE Encodes how your kernel can locate the amount of free
+ space on a disk partition. This can be set to SFS_NONE
+ (0) if you have no way of getting this information,
+ SFS_USTAT (1) if you have the ustat(2) system call,
+ SFS_4ARGS (2) if you have a four-argument statfs(2)
+ system call (and the include file is <sys/statfs.h>),
+ SFS_VFS (3), SFS_MOUNT (4), SFS_STATFS (5) if you have
+ the two-argument statfs(2) system call with includes in
+ <sys/vfs.h>, <sys/mount.h>, or <sys/statfs.h> respectively,
+ or SFS_STATVFS (6) if you have the two-argument statvfs(2)
+ call. The default if nothing is defined is SFS_NONE.
+SFS_BAVAIL with SFS_4ARGS you can also set SFS_BAVAIL to the field name
+ in the statfs structure that holds the useful information;
+ this defaults to f_bavail.
+SPT_TYPE Encodes how your system can display what a process is doing
+ on a ps(1) command (SPT stands for Set Process Title). Can
+ be set to:
+ SPT_NONE (0) -- Don't try to set the process title at all.
+ SPT_REUSEARGV (1) -- Pad out your argv with the information;
+ this is the default if none specified.
+ SPT_BUILTIN (2) -- The system library has setproctitle.
+ SPT_PSTAT (3) -- Use the PSTAT_SETCMD option to pstat(2)
+ to set the process title; this is used by HP-UX.
+ SPT_PSSTRINGS (4) -- Use the magic PS_STRINGS pointer (4.4BSD).
+ SPT_SYSMIPS (5) -- Use sysmips() supported by NEWS-OS 6.
+ SPT_SCO (6) -- Write kernel u. area.
+ SPT_CHANGEARGV (7) -- Write pointers to our own strings into
+ the existing argv vector.
+SPT_PADCHAR Character used to pad the process title; if undefined,
+ the space character (0x20) is used. This is ignored if
+ SPT_TYPE != SPT_REUSEARGV
+ERRLIST_PREDEFINED
+ If set, assumes that some header file defines sys_errlist.
+ This may be needed if you get type conflicts on this
+ variable -- otherwise don't worry about it.
+WAITUNION The wait(2) routine takes a "union wait" argument instead
+ of an integer argument. This is for compatibility with
+ old versions of BSD.
+SCANF You can set this to extend the F command to accept a
+ scanf string -- this gives you a primitive parser for
+ class definitions -- BUT it can make you vulnerable to
+ core dumps if the target file is poorly formed.
+SYSLOG_BUFSIZE You can define this to be the size of the buffer that
+ syslog accepts. If it is not defined, it assumes a
+ 1024-byte buffer. If the buffer is very small (under
+ 256 bytes) the log message format changes -- each
+ e-mail message will log many more messages, since it
+ will log each piece of information as a separate line
+ in syslog.
+BROKEN_RES_SEARCH
+ On Ultrix (and maybe other systems?) if you use the
+ res_search routine with an unknown host name, it returns
+ -1 but sets h_errno to 0 instead of HOST_NOT_FOUND. If
+ you set this, sendmail considers 0 to be the same as
+ HOST_NOT_FOUND.
+NAMELISTMASK If defined, values returned by nlist(3) are masked
+ against this value before use -- a common value is
+ 0x7fffffff to strip off the top bit.
+BSD4_4_SOCKADDR If defined, socket addresses have an sa_len field that
+ defines the length of this address.
+SAFENFSPATHCONF Set this to 1 if and only if you have verified that a
+ pathconf(2) call with _PC_CHOWN_RESTRICTED argument on an
+ NFS filesystem where the underlying system allows users to
+ give away files to other users returns <= 0. Be sure you
+ try both on NFS V2 and V3. Some systems assume that their
+ local policy apply to NFS servers -- this is a bad
+ assumption! The test/t_pathconf.c program will try this
+ for you -- you have to run it in a directory that is
+ mounted from a server that allows file giveaway.
+SIOCGIFCONF_IS_BROKEN
+ Set this if your system has an SIOCGIFCONF ioctl defined,
+ but it doesn't behave the same way as "most" systems (BSD,
+ Solaris, SunOS, HP-UX, etc.)
+SIOCGIFNUM_IS_BROKEN
+ Set this if your system has an SIOCGIFNUM ioctl defined,
+ but it doesn't behave the same way as "most" systems
+ (Solaris, HP-UX).
+NEED_PERCENTQ Set this if your system doesn't support the printf
+ format strings %lld or %llu. If this is set, %qd and
+ %qu are used instead.
+
+
+
++-----------------------+
+| COMPILE-TIME FEATURES |
++-----------------------+
+
+There are a bunch of features that you can decide to compile in, such
+as selecting various database packages and special protocol support.
+Several are assumed based on other compilation flags -- if you want to
+"un-assume" something, you probably need to edit conf.h. Compilation
+flags that add support for special features include:
+
+NDBM Include support for "new" DBM library for aliases and maps.
+ Normally defined in the Makefile.
+NEWDB Include support for Berkeley DB package (hash & btree)
+ for aliases and maps. Normally defined in the Makefile.
+ If the version of NEWDB you have is the old one that does
+ not include the "fd" call (this call was added in version
+ 1.5 of the Berkeley DB code), you must upgrade to the
+ current version of Berkeley DB.
+NIS Define this to get NIS (YP) support for aliases and maps.
+ Normally defined in the Makefile.
+NISPLUS Define this to get NIS+ support for aliases and maps.
+ Normally defined in the Makefile.
+HESIOD Define this to get Hesiod support for aliases and maps.
+ Normally defined in the Makefile.
+NETINFO Define this to get NeXT NetInfo support for aliases and maps.
+ Normally defined in the Makefile.
+USERDB Define this to 1 to include support for the User Information
+ Database. Implied by NEWDB or HESIOD. You can use
+ -DUSERDB=0 to explicitly turn it off.
+IDENTPROTO Define this as 1 to get IDENT (RFC 1413) protocol support.
+ This is assumed unless you are running on Ultrix or
+ HP-UX, both of which have a problem in the UDP
+ implementation. You can define it to be 0 to explicitly
+ turn off IDENT protocol support. If defined off, the code
+ is actually still compiled in, but it defaults off; you
+ can turn it on by setting the IDENT timeout to 30s in the
+ configuration file.
+IP_SRCROUTE Define this to 1 to get IP source routing information
+ displayed in the Received: header. This is assumed on
+ most systems, but some (e.g., Ultrix) apparently have a
+ broken version of getsockopt that doesn't properly
+ support the IP_OPTIONS call. You probably want this if
+ your OS can cope with it. Symptoms of failure will be that
+ it won't compile properly (that is, no support for fetching
+ IP_OPTIONs), or it compiles but source-routed TCP connections
+ either refuse to open or open and hang for no apparent reason.
+ Ultrix and AIX3 are known to fail this way.
+LOG Set this to get syslog(3) support. Defined by default
+ in conf.h. You want this if at all possible.
+NETINET Set this to get TCP/IP support. Defined by default
+ in conf.h. You probably want this.
+NETISO Define this to get ISO networking support.
+NETUNIX Define this to get Unix domain networking support. Defined
+ by default. A few bizarre systems (SCO, ISC, Altos) don't
+ support this networking domain.
+SMTP Define this to get the SMTP code. Implied by NETINET
+ or NETISO.
+NAMED_BIND If non-zero, include DNS (name daemon) support, including
+ MX support. The specs say you must use this if you run
+ SMTP. You don't have to be running a name server daemon
+ on your machine to need this -- any use of the DNS resolver,
+ including remote access to another machine, requires this
+ option. Defined by default in conf.h. Define it to zero
+ ONLY on machines that do not use DNS in any way.
+QUEUE Define this to get queueing code. Implied by NETINET
+ or NETISO; required by SMTP. This gives you other good
+ stuff -- it should be on.
+DAEMON Define this to get general network support. Implied by
+ NETINET or NETISO. Defined by default in conf.h. You
+ almost certainly want it on.
+MATCHGECOS Permit fuzzy matching of user names against the full
+ name (GECOS) field in the /etc/passwd file. This should
+ probably be on, since you can disable it from the config
+ file if you want to. Defined by default in conf.h.
+MIME8TO7 If non-zero, include 8 to 7 bit MIME conversions. This
+ also controls advertisement of 8BITMIME in the ESMTP
+ startup dialogue.
+MIME7TO8 If non-zero, include 7 to 8 bit MIME conversions.
+HES_GETMAILHOST Define this to 1 if you are using Hesiod with the
+ hes_getmailhost() routine. This is included with the MIT
+ Hesiod distribution, but not with the DEC Hesiod distribution.
+XDEBUG Do additional internal checking. These don't cost too
+ much; you might as well leave this on.
+TCPWRAPPERS Turns on support for the TCP wrappers library (-lwrap).
+ See below for further information.
+SECUREWARE Enable calls to the SecureWare luid enabling/changing routines.
+ SecureWare is a C2 security package added to several UNIX's
+ (notably ConvexOS) to get a C2 Secure system. This
+ option causes mail delivery to be done with the luid of the
+ recipient.
+SHARE_V1 Support for the fair share scheduler, version 1. Setting to
+ 1 causes final delivery to be done using the recipients
+ resource limitations. So far as I know, this is only
+ supported on ConvexOS.
+
+
++---------------------+
+| DNS/RESOLVER ISSUES |
++---------------------+
+
+Many systems have old versions of the resolver library. At a minimum,
+you should be running BIND 4.8.3; older versions may compile, but they
+have known bugs that should give you pause.
+
+Common problems in old versions include "undefined" errors for
+dn_skipname.
+
+Some people have had a problem with BIND 4.9; it uses some routines
+that it expects to be externally defined such as strerror(). It may
+help to link with "-l44bsd" to solve this problem. This has apparently
+been fixed in later versions of BIND, starting around 4.9.3. In other
+words, if you use 4.9.0 through 4.9.2, you need -l44bsd; for earlier or
+later versions, you do not.
+
+!PLEASE! be sure to link with the same version of the resolver as
+the header files you used -- some people have used the 4.9 headers
+and linked with BIND 4.8 or vice versa, and it doesn't work.
+Unfortunately, it doesn't fail in an obvious way -- things just
+subtly don't work.
+
+WILDCARD MX RECORDS ARE A BAD IDEA! The only situation in which they
+work reliably is if you have two versions of DNS, one in the real world
+which has a wildcard pointing to your firewall, and a completely
+different version of the database internally that does not include
+wildcard MX records that match your domain. ANYTHING ELSE WILL GIVE
+YOU HEADACHES!
+
+
++-------------------------------------+
+| OPERATING SYSTEM AND COMPILE QUIRKS |
++-------------------------------------+
+
+GCC problems
+ *****************************************************************
+ ** IMPORTANT: DO NOT USE OPTIMIZATION (``-O'') IF YOU ARE **
+ ** RUNNING GCC 2.4.x or 2.5.x. THERE IS A BUG IN THE GCC **
+ ** OPTIMIZER THAT CAUSES SENDMAIL COMPILES TO FAIL MISERABLY. **
+ *****************************************************************
+
+ Jim Wilson of Cygnus believes he has found the problem -- it will
+ probably be fixed in GCC 2.5.6 -- but until this is verified, be
+ very suspicious of gcc -O. This problem is reported to have been
+ fixed in gcc 2.6.
+
+ A bug in gcc 2.5.5 caused problems compiling sendmail 8.6.5 with
+ optimization on a Sparc. If you are using gcc 2.5.5, youi should
+ upgrade to the latest version of gcc.
+
+ Apparently GCC 2.7.0 on the Pentium processor has optimization
+ problems. I recommend against using -O on that architecture. This
+ has been seen on FreeBSD 2.0.5 RELEASE.
+
+ Solaris 2.X users should use version 2.7.2.3 over 2.7.2.
+
+ We have been told there are problems with gcc 2.8.0. If you are
+ using this version, you should upgrade to 2.8.1 or later.
+
+GDBM GDBM does not work with sendmail 8.8 because the additional
+ security checks and file locking cause problems. Unfortunately,
+ gdbm does not provide a compile flag in its version of ndbm.h so
+ the code can adapt. Until the GDBM authors can fix these problems,
+ GDBM will not be supported. Please use Berkeley DB instead.
+
+Configuration file location
+ Up to 8.6, sendmail tried to find the sendmail.cf file in the same
+ place as the vendors had put it, even when this was obviously
+ stupid. As of 8.7, sendmail ALWAYS looks for /etc/sendmail.cf.
+ Beginning with 8.10, sendmail will use /etc/mail/sendmail.cf.
+ You can get sendmail to use the stupid vendor .cf location by
+ adding -DUSE_VENDOR_CF_PATH during compilation, but this may break
+ support programs and scripts that need to find sendmail.cf. You
+ are STRONGLY urged to use symbolic links if you want to use the
+ vendor location rather than changing the location in the sendmail
+ binary.
+
+SunOS 4.x (Solaris 1.x)
+ You may have to use -lresolv on SunOS. However, beware that
+ this links in a new version of gethostbyname that does not
+ understand NIS, so you must have all of your hosts in DNS.
+
+ Some people have reported problems with the SunOS version of
+ -lresolv and/or in.named, and suggest that you get a newer
+ version. The symptoms are delays when you connect to the
+ SMTP server on a SunOS machine or having your domain added to
+ addresses inappropriately. There is a version of BIND
+ version 4.9 on gatekeeper.DEC.COM in pub/BSD/bind/4.9.
+
+ There is substantial disagreement about whether you can make
+ this work with resolv+, which allows you to specify a search-path
+ of services. Some people report that it works fine, others
+ claim it doesn't work at all (including causing sendmail to
+ drop core when it tries to do multiple resolv+ lookups for a
+ single job). I haven't tried resolv+, as we use DNS exclusively.
+
+ Should you want to try resolv+, it is on ftp.uu.net in
+ /networking/ip/dns.
+
+ Apparently getservbyname() can fail under moderate to high
+ load under some circumstances. This will exhibit itself as
+ the message ``554 makeconnection: service "smtp" unknown''.
+ The problem has been traced to one or more blank lines in
+ /etc/services on the NIS server machine. Delete these
+ and it should work. This info is thanks to Brian Bartholomew
+ <bb@math.ufl.edu> of I-Kinetics, Inc.
+
+SunOS 4.0.2 (Sun 386i)
+ Date: Fri, 25 Aug 1995 11:13:58 +0200 (MET DST)
+ From: teus@oce.nl
+
+ Sendmail 8.7.Beta.12 compiles and runs nearly out of the box with the
+ following changes:
+ * Don't use /usr/5bin in your PATH, but make /usr/5bin/uname
+ available as "uname" command.
+ * Use the defines "-DBSD4_3 -DNAMED_BIND=0" in
+ BuildTools/OS/SunOS.4.0, which is selected via the "uname" command.
+ I recommend to make available the db-library on the system first
+ (and change the Makefile to use this library).
+ Note that the sendmail.cf and aliases files are found in /etc.
+
+SunOS 4.1.3, 4.1.3_U1
+ Sendmail causes crashes on SunOS 4.1.3 and 4.1.3_U1. According
+ to Sun bug number 1077939:
+
+ If an application does a getsockopt() on a SOCK_STREAM (TCP) socket
+ after the other side of the connection has sent a TCP RESET for
+ the stream, the kernel gets a Bus Trap in the tcp_ctloutput() or
+ ip_ctloutput() routine.
+
+ For 4.1.3, this is fixed in patch 100584-08, available on the
+ Sunsolve 2.7.1 or later CDs. For 4.1.3_U1, this was fixed in patch
+ 101790-01 (SunOS 4.1.3_U1: TCP socket and reset problems), later
+ obsoleted by patch 102010-05.
+
+ Sun patch 100584-08 is not currently publicly available on their
+ ftp site but a user has reported it can be found at other sites
+ using a web search engine.
+
+Solaris 2.x (SunOS 5.x)
+ To compile for Solaris, the Makefile built by Build must
+ include a SOLARIS definition which reflects the Solaris version
+ (i.e. -DSOLARIS=20400 for 2.4 or -DSOLARIS=20501 for 2.5.1).
+ If you are using gcc, make sure -I/usr/include is not used (or
+ it might complain about TopFrame). If you are using Sun's cc,
+ make sure /opt/SUNWspro/bin/cc is used instead of /usr/ucb/cc
+ (or it might complain about tm_zone).
+
+ To the best of my knowledge, Solaris does not have the
+ gethostbyname problem described above. However, it does
+ have another one:
+
+ From a correspondent:
+
+ For solaris 2.2, I have
+
+ hosts: files dns
+
+ in /etc/nsswitch.conf and /etc/hosts has to have the fully
+ qualified host name. I think "files" has to be before "dns"
+ in /etc/nsswitch.conf during bootup.
+
+ From another correspondent:
+
+ When running sendmail under Solaris, the gethostbyname()
+ hack in conf.c which should perform proper canonicalization
+ of host names could fail. Result: the host name is not
+ canonicalized despite the hack, and you'll have to define $j
+ and $m in sendmail.cf somewhere.
+
+ The reason could be that /etc/nsswitch.conf is improperly
+ configured (at least from sendmail's point of view). For
+ example, the line
+
+ hosts: files nisplus dns
+
+ will make gethostbyname() look in /etc/hosts first, then ask
+ nisplus, then dns. However, if /etc/hosts does not contain
+ the full canonicalized hostname, then no amount of
+ gethostbyname()s will work.
+
+ Solution (or rather, a workaround): Ask nisplus first, then
+ dns, then local files:
+
+ hosts: nisplus dns [NOTFOUND=return] files
+
+ The Solaris "syslog" function is apparently limited to something
+ about 90 characters because of a kernel limitation. If you have
+ source code, you can probably up this number. You can get patches
+ that fix this problem: the patch ids are:
+
+ Solaris 2.1 100834
+ Solaris 2.2 100999
+ Solaris 2.3 101318
+
+ Be sure you have the appropriate patch installed or you won't
+ see system logging.
+
+Solaris 2.4 (SunOS 5.4)
+ If you include /usr/lib at the end of your LD_LIBRARY_PATH you run
+ the risk of getting the wrong libraries under some circumstances.
+ This is because of a new feature in Solaris 2.4, described by
+ Rod.Evans@Eng.Sun.COM:
+
+ >> Prior to SunOS 5.4, any LD_LIBRARY_PATH setting was ignored by the
+ >> runtime linker if the application was setxid (secure), thus your
+ >> applications search path would be:
+ >>
+ >> /usr/local/lib LD_LIBRARY_PATH component - IGNORED
+ >> /usr/lib LD_LIBRARY_PATH component - IGNORED
+ >> /usr/local/lib RPATH - honored
+ >> /usr/lib RPATH - honored
+ >>
+ >> the effect is that path 3 would be the first used, and this would
+ >> satisfy your resolv.so lookup.
+ >>
+ >> In SunOS 5.4 we made the LD_LIBRARY_PATH a little more flexible.
+ >> People who developed setxid applications wanted to be able to alter
+ >> the library search path to some degree to allow for their own
+ >> testing and debugging mechanisms. It was decided that the only
+ >> secure way to do this was to allow a `trusted' path to be used in
+ >> LD_LIBRARY_PATH. The only trusted directory we presently define
+ >> is /usr/lib. Thus a setuid root developer could play with some
+ >> alternative shared object implementations and place them in
+ >> /usr/lib (being root we assume they'ed have access to write in this
+ >> directory). This change was made as part of 1155380 - after a
+ >> *huge* amount of discussion regarding the security aspect of things.
+ >>
+ >> So, in SunOS 5.4 your applications search path would be:
+ >>
+ >> /usr/local/lib from LD_LIBRARY_PATH - IGNORED (untrustworthy)
+ >> /usr/lib from LD_LIBRARY_PATH - honored (trustworthy)
+ >> /usr/local/lib from RPATH - honored
+ >> /usr/lib from RPATH - honored
+ >>
+ >> here, path 2 would be the first used.
+
+Solaris 2.6 (SunOS 5.6)
+ If you built sendmail 8.8.1 through 8.8.4 inclusive on a Solaris 2.5
+ system, that binary will not run on Solaris 2.6, due to problems with
+ incompatible snprintf(3s) calls. This problem is fixed in sendmail
+ 8.8.5.
+
+Solaris 2.5.1 (SunOS 5.5.1) and 2.6 (SunOS 5.6)
+ Apparently Solaris 2.5.1 patch 103663-01 installs a new
+ /usr/include/resolv.h file that defines the __P macro without
+ checking to see if it is already defined. This new resolv.h is also
+ included in the Solaris 2.6 distribution. This causes compile
+ warnings such as:
+
+ In file included from daemon.c:51:
+ /usr/include/resolv.h:208: warning: `__P' redefined
+ cdefs.h:58: warning: this is the location of the previous definition
+
+ These warnings can be safely ignored or you can create a resolv.h
+ file in the obj.SunOS.5.5.1.* or obj.SunOS.5.6.* directory that reads:
+
+ #undef __P
+ #include "/usr/include/resolv.h"
+
+ Sun is aware of the problem (Sun bug ID 4081053) and it will be fixed
+ in Solaris 2.7.
+
+Ultrix
+ By default, the IDENT protocol is turned off on Ultrix. If you
+ are running Ultrix 4.4 or later, or if you have included patch
+ CXO-8919 for Ultrix 4.2 or 4.3 to fix the TCP problem, you can turn
+ IDENT on in the configuration file by setting the "ident" timeout
+ to 30 seconds.
+
+Digital UNIX (formerly DEC OSF/1)
+ If you are compiling on OSF/1 (DEC Alpha), you must use
+ -L/usr/shlib (otherwise it core dumps on startup). You may also
+ need -mld to get the nlist() function, although some versions
+ apparently don't need this.
+
+ Also, the enclosed makefile removed /usr/sbin/smtpd; if you need
+ it, just create the link to the sendmail binary.
+
+ On DEC OSF/1 3.2 or earlier, the MatchGECOS option doesn't work
+ properly due to a bug in the getpw* routines. If you want to use
+ this, use -DDEC_OSF_BROKEN_GETPWENT=1. The problem is fixed in 3.2C.
+
+ Digital's mail delivery agent, /bin/mail (aka /bin/binmail), will
+ only preserve the envelope sender in the "From " header if
+ DefaultUserID is set to daemon. Setting this to mailnull will
+ cause all mail to have the header "From mailnull ...". To use
+ a different DefaultUserID, you will need to use a different mail
+ delivery agent (such as mail.local found in the sendmail
+ distribution).
+
+ On Digital UNIX 4.0 and later, Berkeley DB 1.85 is included with the
+ operating system and already has the ndbm.o module removed. However,
+ Digital has modified the original Berkeley DB db.h include file.
+ This results in the following warning while compiling map.c and udb.c:
+
+ cc: Warning: /usr/include/db.h, line 74: The redefinition of the macro
+ "__signed" conflicts with a current definition because the replacement
+ lists differ. The redefinition is now in effect.
+ #define __signed signed
+ ------------------------^
+
+ This warning can be ignored.
+
+IRIX
+ The header files on SGI IRIX are completely prototyped, and as
+ a result you can sometimes get some warning messages during
+ compilation. These can be ignored. There are two errors in
+ deliver only if you are using gcc, both of the form ``warning:
+ passing arg N of `execve' from incompatible pointer type''.
+ Also, if you compile with -DNIS, you will get a complaint
+ about a declaration of struct dom_binding in a prototype
+ when compiling map.c; this is not important because the
+ function being prototyped is not used in that file.
+
+ In order to compile sendmail you will have had to install
+ the developers' option in order to get the necessary include
+ files.
+
+ If you compile with -lmalloc (the fast memory allocator), you may
+ get warning messages such as the following:
+
+ ld32: WARNING 85: definition of _calloc in /usr/lib32/libmalloc.so
+ preempts that definition in /usr/lib32/mips3/libc.so.
+ ld32: WARNING 85: definition of _malloc in /usr/lib32/libmalloc.so
+ preempts that definition in /usr/lib32/mips3/libc.so.
+ ld32: WARNING 85: definition of _realloc in /usr/lib32/libmalloc.so
+ preempts that definition in /usr/lib32/mips3/libc.so.
+ ld32: WARNING 85: definition of _free in /usr/lib32/libmalloc.so
+ preempts that definition in /usr/lib32/mips3/libc.so.
+ ld32: WARNING 85: definition of _cfree in /usr/lib32/libmalloc.so
+ preempts that definition in /usr/lib32/mips3/libc.so.
+
+ These are unavoidable and innocuous -- just ignore them.
+
+ According to Dave Sill <de5@ornl.gov>, there is a version of the
+ Berkeley DB library patched to run on Irix 6.2 available from
+ http://reality.sgi.com/ariel/freeware/#db .
+
+IRIX 6.x
+ It is important that on IRIX 6.x you give used ABI in command
+ line of Build, otherwise configuration script does not work
+ correctly, e.g.,
+
+ sh Build -E ABI=-n32
+
+ If you are using XFS filesystem, avoid using ABI=-32 if possible.
+
+NeXT or NEXTSTEP
+ NEXTSTEP 3.3 and earlier ship with the old DBM library. Also,
+ Berkeley DB does not currently run on NEXTSTEP.
+
+ If you are compiling on NEXTSTEP, you will have to create an
+ empty file "unistd.h" and create a file "dirent.h" containing:
+
+ #include <sys/dir.h>
+ #define dirent direct
+
+ (BuildTools/OS/NeXT should try to do both of these for you.)
+
+ Apparently, there is a bug in getservbyname on Nextstep 3.0
+ that causes it to fail under some circumstances with the
+ message "SYSERR: service "smtp" unknown" logged. You should
+ be able to work around this by including the line:
+
+ OOPort=25
+
+ in your .cf file.
+
+ You may have to use -DNeXT.
+
+BSDI (BSD/386) 1.0, NetBSD 0.9, FreeBSD 1.0
+ The "m4" from BSDI won't handle the config files properly.
+ I haven't had a chance to test this myself.
+
+ The M4 shipped in FreeBSD and NetBSD 0.9 don't handle the config
+ files properly. One must use either GNU m4 1.1 or the PD-M4
+ recently posted in comp.os.386bsd.bugs (and maybe others).
+ NetBSD-current includes the PD-M4 (as stated in the NetBSD file
+ CHANGES).
+
+ FreeBSD 1.0 RELEASE has uname(2) now. Use -DUSEUNAME in order to
+ use it (look into BuildTools/OS/FreeBSD). NetBSD-current may have
+ it too but it has not been verified.
+
+ The latest version of Berkeley DB uses a different naming
+ scheme than the version that is supplied with your release. This
+ means you will be able to use the current version of Berkeley DB
+ with sendmail as long you use the new db.h when compiling
+ sendmail and link it against the new libdb.a. You should probably
+ keep the original db.h in /usr/include and the new db.h in
+ /usr/local/include.
+
+4.3BSD
+ If you are running a "virgin" version of 4.3BSD, you'll have
+ a very old resolver and be missing some header files. The
+ header files are simple -- create empty versions and everything
+ will work fine. For the resolver you should really port a new
+ version (4.8.3 or later) of the resolver; 4.9 is available on
+ gatekeeper.DEC.COM in pub/BSD/bind/4.9. If you are really
+ determined to continue to use your old, buggy version (or as
+ a shortcut to get sendmail working -- I'm sure you have the
+ best intentions to port a modern version of BIND), you can
+ copy ../contrib/oldbind.compat.c into src and add
+ oldbind.compat.o to OBJADD in the Makefile.
+
+A/UX
+ Date: Tue, 12 Oct 1993 18:28:28 -0400 (EDT)
+ From: "Eric C. Hagberg" <hagberg@med.cornell.edu>
+ Subject: Fix for A/UX ndbm
+
+ I guess this isn't really a sendmail bug, however, it is something
+ that A/UX users should be aware of when compiling sendmail 8.6.
+
+ Apparently, the calls that sendmail is using to the ndbm routines
+ in A/UX 3.0.x contain calls to "broken" routines, in that the
+ aliases database will break when it gets "just a little big"
+ (sorry I don't have exact numbers here, but it broke somewhere
+ around 20-25 aliases for me.), making all aliases non-functional
+ after exceeding this point.
+
+ What I did was to get the gnu-dbm-1.6 package, compile it, and
+ then re-compile sendmail with "-lgdbm", "-DNDBM", and using the
+ ndbm.h header file that comes with the gnu-package. This makes
+ things behave properly.
+ [NOTE: see comment above about GDBM]
+
+ I suppose porting the New Berkeley DB package is another route,
+ however, I made a quick attempt at it, and found it difficult
+ (not easy at least); the gnu-dbm package "configured" and
+ compiled easily.
+
+ [NOTE: Berkeley DB version 2.X runs on A/UX and can be used for
+ database maps.]
+
+SCO Unix
+ From: Thomas Essebier <tom@stallion.oz.au>
+ Organisation: Stallion Technologies Pty Ltd.
+
+ It will probably help those who are trying to configure sendmail 8.6.9
+ to know that if they are on SCO, they had better set
+ OI-dnsrch
+ or they will core dump as soon as they try to use the resolver.
+ ie. although SCO has _res.dnsrch defined, and is kinda BIND 4.8.3, it
+ does not inititialise it, nor does it understand 'search' in
+ /etc/named.boot.
+ - sigh -
+
+ According to SCO, the m4 which ships with UnixWare 2.1.2 is broken.
+ We recommend installing GNU m4 before attempting to build sendmail.
+
+DG/UX
+ Doug Anderson <dlander@afterlife.ncsc.mil> has successfully run
+ V8 on the DG/UX 5.4.2 and 5.4R3.x platforms under heavy usage.
+ Originally, the DG /bin/mail program wasn't compatible with
+ the V8 sendmail, since the DG /bin/mail requires the environment
+ variable "_FORCE_MAIL_LOCAL_=yes" be set. Version 8.7 now includes
+ this in the environment before invoking the local mailer. Some
+ have used procmail to avoid this problem in the past. It works
+ but some have experienced file locking problems with their DG/UX
+ ports of procmail.
+
+Apollo DomainOS
+ If you are compiling on Apollo, you will have to create an empty
+ file "unistd.h" (for DomainOS 10.3 and earlier) and create a file
+ "dirent.h" containing:
+
+ #include <sys/dir.h>
+ #define dirent direct
+
+ (BuildTools/OS/DomainOS will attempt to do both of these for you.)
+
+HP-UX 8.00
+ Date: Mon, 24 Jan 1994 13:25:45 +0200
+ From: Kimmo Suominen <Kimmo.Suominen@lut.fi>
+ Subject: 8.6.5 w/ HP-UX 8.00 on s300
+
+ Just compiled and fought with sendmail 8.6.5 on a HP9000/360 (ie. a
+ series 300 machine) running HP-UX 8.00.
+
+ I was getting segmentation fault when delivering to a local user.
+ With debugging I saw it was faulting when doing _free@libc... *sigh*
+ It seems the new implementation of malloc on s300 is buggy as of 8.0,
+ so I tried out the one in -lmalloc (malloc(3X)). With that it seems
+ to work just dandy.
+
+ When linking, you will get the following error:
+
+ ld: multiply defined symbol _freespace in file /usr/lib/libmalloc.a
+
+ but you can just ignore it. You might want to add this info to the
+ README file for the future...
+
+Linux
+ Something broke between versions 0.99.13 and 0.99.14 of Linux:
+ the flock() system call gives errors. If you are running .14,
+ you must not use flock. You can do this with -DHASFLOCK=0.
+
+ Around the inclusion of bind-4.9.3 & Linux libc-4.6.20, the
+ initialization of the _res structure changed. If /etc/hosts.conf
+ was configured as "hosts, bind" the resolver code could return
+ "Name server failure" errors. This is supposedly fixed in
+ later versions of libc (>= 4.6.29?), and later versions of
+ sendmail (> 8.6.10) try to work around the problem.
+
+ Some older versions (< 4.6.20?) of the libc/include files conflict
+ with sendmail's version of cdefs.h. Deleting sendmail's version
+ on those systems should be non-harmful, and new versions don't care.
+
+ Sendmail assumes that libc has snprintf, which has been true since
+ libc 4.7.0. If you are running an older version, you will need to
+ use -DHASSNPRINTF=0 in the Makefile. If may be able to use -lbsd
+ (which includes snprintf) instead of turning this off on versions
+ of libc between 4.4.4 and 4.7.0 (snprintf improves security, so
+ you want to use this if at all possible).
+
+ NOTE ON LINUX & BIND: By default, the Makefile generated for Linux
+ includes header files in /usr/local/include and libraries in
+ /usr/local/lib. If you've installed BIND on your system, the header
+ files typically end up in the search path and you need to add
+ "-lresolv" to the LIBS line in your Makefile. Really old versions
+ may need to include "-l44bsd" as well (particularly if the link phase
+ complains about missing strcasecmp, strncasecmp or strpbrk).
+ Complaints about an undefined reference to `__dn_skipname' in
+ domain.o are a sure sign that you need to add -lresolv to LIBS.
+ Newer versions of Linux are basically threaded BIND, so you may or
+ may not see complaints if you accidentally mix BIND
+ headers/libraries with virginal libc. If you have BIND headers in
+ /usr/local/include (resolv.h, etc) you *should* be adding -lresolv
+ to LIBS. Data structures may change and you'd be asking for a
+ core dump.
+
+AIX 3.x
+ This version of sendmail does not support MB, MG, and MR resource
+ records, which are supported by AIX sendmail.
+
+ Several people have reported that the IBM-supplied named returns
+ fairly random results -- the named should be replaced. It is not
+ necessary to replace the resolver, which will simplify installation.
+ A new BIND resolver can be found at http://www.isc.org/isc/.
+
+AIX 3.1.x
+ The supplied load average code only works correctly for AIX 3.2.x.
+ For 3.1, use -DLA_TYPE=LA_SUBR and get the latest ``monitor''
+ package by Jussi Maki <jmaki@hut.fi> from ftp.funet.fi in the
+ directory pub/unix/AIX/rs6000/monitor-1.12.tar.Z; use the loadavgd
+ daemon, and the getloadavg subroutine supplied with that package.
+ If you don't care about load average throttling, just turn off
+ load average checking using -DLA_TYPE=LA_ZERO.
+
+AIX 2.2.1
+ Date: Mon Dec 4 14:14:56 CST 1995
+ From: Mark Whetzel <markw@antimatr.houston.tx.us>
+ Subject: Porting sendmail 8.7.2 to AIX V2 on the RT.
+
+ This version of sendmail does not support MB, MG, and MR resource
+ records, which are supported by AIX sendmail.
+
+ AIX V2 on the RT does not have 'paths.h'. Create a null
+ file in the 'obj' directory to remove this compile error.
+
+ A patch file is needed to get the BSD 'db' library to compile
+ for AIX/RT. I have sent the necessary updates to the author,
+ but they may not be immediately available.
+ [NOTE: Berkeley DB version 2.X runs on AIX/RT.]
+
+ The original AIX/RT resolver libraries are very old, and you
+ should get the latest BIND to replace it. The 4.8.3 version
+ has been tested, but 4.9.x is out and should work.
+
+ To make the load average code work correctly requires an
+ external routine, as the kernel does not maintain system
+ load averages, similar to AIX V3.1.x. A reverse port of the
+ older 1.05 'monitor' load average daemon code written by
+ Jussi Maki that will work on AIX V2 for the RT is available
+ by E-mail to Mark Whetzel <markw@antimatr.houston.tx.us>.
+ That code depends on an external daemon to collect system
+ load information, and the external routine 'getloadavg',
+ that will return that information. The 'LA_SUBR' define
+ will handle this for AIX V2 on the RT.
+
+ Note: You will have to change BuildTools/OS/AIX.2 to correctly
+ point to the locatons of the updated BIND source tree and
+ the location of the 'newdb' tree and library location.
+ You will also have to change BuildTools/OS/AIX.2 to know
+ about the location of the 'getloadavg' routine if you use
+ the LA_SUBR define.
+
+
+ Manual pages will format correctly if given the mandoc macros
+ and used with nroff. I have not tried groff.
+
+RISC/os
+ RISC/os from MIPS is a merged AT&T/Berkeley system. When you
+ compile on that platform you will get duplicate definitions
+ on many files. You can ignore these.
+
+System V Release 4 Based Systems
+ There is a single BuildTools OS that is intended for all SVR4-based
+ systems (built from BuildTools/OS/SVR4). It defines __svr4__,
+ which is predefined by some compilers. If your compiler already
+ defines this compile variable, you can delete the definition from
+ the generated Makefile or create a BuildTools/Site/site.config.m4
+ file.
+
+ It's been tested on Dell Issue 2.2.
+
+DELL SVR4
+ Date: Mon, 06 Dec 1993 10:42:29 EST
+ From: "Kimmo Suominen" <kim@grendel.lut.fi>
+ Message-ID: <2d0352f9.lento29@lento29.UUCP>
+ To: eric@cs.berkeley.edu
+ Cc: sendmail@cs.berkeley.edu
+ Subject: Notes for DELL SVR4
+
+ Eric,
+
+ Here are some notes for compiling Sendmail 8.6.4 on DELL SVR4. I ran
+ across these things when helping out some people who contacted me by
+ e-mail.
+
+ 1) Use gcc 2.4.5 (or later?). Dell distributes gcc 2.1 with their
+ Issue 2.2 Unix. It is too old, and gives you problems with
+ clock.c, because sigset_t won't get defined in <sys/signal.h>.
+ This is due to a problematic protection rule in there, and is
+ fixed with gcc 2.4.5.
+
+ 2) If you don't use the new Berkeley DB (-DNEWDB), then you need
+ to add "-lc -lucb" to the libraries to link with. This is because
+ the -ldbm distributed by Dell needs the bcopy, bcmp and bzero
+ functions. It is important that you specify both libraries in
+ the given order to be sure you only get the BSTRING functions
+ from the UCB library (and not the signal routines etc.).
+
+ 3) Don't leave out "-lelf" even if compiling with "-lc -lucb".
+ The UCB library also has another copy of the nlist routines,
+ but we do want the ones from "-lelf".
+
+ If anyone needs a compiled gcc 2.4.5 and/or a ported DB library, they
+ can use anonymous ftp to fetch them from lut.fi in the /kim directory.
+ They are copies of what I use on grendel.lut.fi, and offering them
+ does not imply that I would also support them. I have sent the DB
+ port for SVR4 back to Keith Bostic for inclusion in the official
+ distribution, but I haven't heard anything from him as of today.
+
+ - gcc-2.4.5-svr4.tar.gz (gcc 2.4.5 and the corresponding libg++)
+ - db-1.72.tar.gz (with source, objects and a installed copy)
+
+ Cheers
+ + Kim
+ --
+ * Kimmo.Suominen@lut.fi * SysVr4 enthusiast at GRENDEL.LUT.FI *
+ * KIM@FINFILES.BITNET * Postmaster and Hostmaster at LUT.FI *
+ * + 358 200 865 718 * Unix area moderator at NIC.FUNET.FI *
+
+ConvexOS 10.1 and below
+ In order to use the name server, you must create the file
+ /etc/use_nameserver. If this file does not exist, the call
+ to res_init() will fail and you will have absolutely no
+ access to DNS, including MX records.
+
+Amdahl UTS 2.1.5
+ In order to get UTS to work, you will have to port BIND 4.9.
+ The vendor's BIND is reported to be ``totally inadequate.''
+ See sendmail/contrib/AmdahlUTS.patch for the patches necessary
+ to get BIND 4.9 compiled for UTS.
+
+UnixWare
+ According to Alexander Kolbasov <sasha@unitech.gamma.ru>,
+ the m4 on UnixWare 2.0 (still in Beta) will core dump on the
+ config files. GNU m4 and the m4 from UnixWare 1.x both work.
+
+ According to Larry Rosenman <ler@lerami.lerctr.org>:
+
+ UnixWare 2.1.[23]'s m4 chokes (not obviously) when
+ processing the 8.9.0 cf files.
+
+ I had a LOCAL_RULE_0 that wound up AFTER the
+ SBasic_check_rcpt rules using the SCO supplied M4.
+ GNU M4 works fine.
+
+UNICOS 8.0.3.4
+ Some people have reported that the -O flag on UNICOS can cause
+ problems. You may want to turn this off if you have problems
+ running sendmail. Reported by Jerry G. DeLapp <jgd@acl.lanl.gov>.
+
+GNU getopt
+ I'm told that GNU getopt has a problem in that it gets confused
+ by the double call. Use the version in conf.c instead.
+
+BIND 4.9.2 and Ultrix
+ If you are running on Ultrix, be sure you read conf/Info.Ultrix
+ in the BIND distribution very carefully -- there is information
+ in there that you need to know in order to avoid errors of the
+ form:
+
+ /lib/libc.a(gethostent.o): sethostent: multiply defined
+ /lib/libc.a(gethostent.o): endhostent: multiply defined
+ /lib/libc.a(gethostent.o): gethostbyname: multiply defined
+ /lib/libc.a(gethostent.o): gethostbyaddr: multiply defined
+
+ during the link stage.
+
+strtoul
+ Some compilers (notably gcc) claim to be ANSI C but do not
+ include the ANSI-required routine "strtoul". If your compiler
+ has this problem, you will get an error in srvrsmtp.c on the
+ code:
+
+ # ifdef defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
+ e->e_msgsize = strtoul(vp, (char **) NULL, 10);
+ # else
+ e->e_msgsize = strtol(vp, (char **) NULL, 10);
+ # endif
+
+ You can use -DBROKEN_ANSI_LIBRARY to get around this problem.
+
+Listproc 6.0c
+ Date: 23 Sep 1995 23:56:07 GMT
+ Message-ID: <95925101334.~INN-AUMa00187.comp-news@dl.ac.uk>
+ From: alansz@mellers1.psych.berkeley.edu (Alan Schwartz)
+ Subject: Listproc 6.0c + Sendmail 8.7 [Helpful hint]
+
+ Just upgraded to sendmail 8.7, and discovered that listproc 6.0c
+ breaks, because it, by default, sends a blank "HELO" rather than
+ a "HELO hostname" when using the 'system' or 'telnet' mailmethod.
+
+ The fix is to include -DZMAILER in the compilation, which will
+ cause it to use "HELO hostname" (which Z-mail apparently requires
+ as well. :)
+
+LDAP
+ LDAP was provided by Booker Bense <bbense+ldap@stanford.edu> of
+ Stanford University. From Booker:
+
+ - The patch attached to this message implements an Ldap map class.
+ Currently we are using this at stanford to support campus-wide
+ email addressing. More information can be found at
+ http://www.stanford.edu/~bbense/Inst.html.
+
+ - Currently we are using the ldap map as follows:
+
+ Kluser ldapx
+ -h"localhost borax.stanford.edu borate.stanford.edu boron.stanford.edu"
+ -k"mailacceptinggeneralid=%s" -v maildrop
+
+ and in Rule set S5
+
+ # Now attempt to lookup in luser (ldap map)
+ R< $L > $+ $: < $L > $( luser $1 $)
+ R< $* > $+ @ $+ $: < $3 > $2 Rewrite if forward
+
+ - The map definition supports most of the standard Map args plus most
+ of the command line options of ldapsearch. The software is currently
+ limited to only accepting the first entry returned. It expects that
+ the map defines an ldap filter that returns at most 1 valid entry.
+ It requires the ldap and lber libraries from the Umich Ldap3.2
+ release.
+
+ The software has been in production on Solaris.2.5.1 at Stanford
+ for over 2 years.
+
+TCP Wrappers
+ If you are using -DTCPWRAPPERS to get TCP Wrappers support you will
+ also need to install libwrap.a and modify your site.config.m4 file
+ or the generated Makefile to include -lwrap in the LIBS line
+ (make sure that INCDIRS and LIBDIRS point to where the tcpd.h and
+ libwrap.a can be found).
+
+ TCP Wrappers is available on ftp.win.tue.nl in /pub/security;
+ grab tcp_wrappers_<VER>.tar.gz (where <VER> is the highest
+ numbered version).
+
+ If you have alternate MX sites for your site, be sure that all of
+ your MX sites reject the same set of hosts. If not, a bad guy whom
+ you reject will connect to your site, fail, and move on to the next
+ MX site, which will accept the mail for you and forward it on to you.
+
+Regular Expressions (MAP_REGEX)
+ If sendmail linking fails with:
+
+ undefined reference to 'regcomp'
+
+ or sendmail gives an error about a regular expression with:
+
+ pattern-compile-error: : Operation not applicable
+
+ Your libc does not include a running version of POSIX-regex. Use
+ librx or regex.o from the GNU Free Software Foundation,
+ ftp://ftp.gnu.org/pub/gnu/rx-?.?.tar.gz or
+ ftp://ftp.gnu.org/pub/gnu/regex-?.?.tar.gz.
+ You can also use the regex-lib by Henry Spencer,
+ ftp://ftp.funet.fi/pub/languages/C/spencer/regex.shar.gz
+ Make sure, your compiler reads regex.h from the distribution,
+ not from /usr/include, otherwise sendmail will dump a core.
+
+
++--------------+
+| MANUAL PAGES |
++--------------+
+
+The manual pages have been written against the -mandoc macros
+instead of the -man macros. The latest version of groff has them
+included. You can also get a copy from FTP.UU.NET in the directory
+/systems/unix/bsd-sources/share/tmac. groff is available from
+ftp.gnu.org in the /pub/gnu directory.
+
+
++-----------------+
+| DEBUGGING HOOKS |
++-----------------+
+
+As of 8.6.5, sendmail daemons will catch a SIGUSR1 signal and log
+some debugging output (logged at LOG_DEBUG severity). The
+information dumped is:
+
+ * The value of the $j macro.
+ * A warning if $j is not in the set $=w.
+ * A list of the open file descriptors.
+ * The contents of the connection cache.
+ * If ruleset 89 is defined, it is evaluated and the results printed.
+
+This allows you to get information regarding the runtime state of the
+daemon on the fly. This should not be done too frequently, since
+the process of rewriting may lose memory which will not be recovered.
+Also, ruleset 89 may call non-reentrant routines, so there is a small
+non-zero probability that this will cause other problems. It is
+really only for debugging serious problems.
+
+A typical formulation of ruleset 89 would be:
+
+ R$* $@ $>0 some test address
+
+
++-----------------------------+
+| DESCRIPTION OF SOURCE FILES |
++-----------------------------+
+
+The following list describes the files in this directory:
+
+Makefile.m4 A template for constructing a makefile based on the
+ information in the BuildTools directory.
+README This file.
+TRACEFLAGS My own personal list of the trace flags -- not guaranteed
+ to be particularly up to date.
+alias.c Does name aliasing in all forms.
+arpadate.c A subroutine which creates ARPANET standard dates.
+clock.c Routines to implement real-time oriented functions
+ in sendmail -- e.g., timeouts.
+collect.c The routine that actually reads the mail into a temp
+ file. It also does a certain amount of parsing of
+ the header, etc.
+conf.c The configuration file. This contains information
+ that is presumed to be quite static and non-
+ controversial, or code compiled in for efficiency
+ reasons. Most of the configuration is in sendmail.cf.
+conf.h Configuration that must be known everywhere.
+convtime.c A routine to sanely process times.
+daemon.c Routines to implement daemon mode. This version is
+ specifically for Berkeley 4.1 IPC.
+deliver.c Routines to deliver mail.
+domain.c Routines that interface with DNS (the Domain Name
+ System).
+err.c Routines to print error messages.
+envelope.c Routines to manipulate the envelope structure.
+headers.c Routines to process message headers.
+macro.c The macro expander. This is used internally to
+ insert information from the configuration file.
+main.c The main routine to sendmail. This file also
+ contains some miscellaneous routines.
+map.c Support for database maps.
+mci.c Routines that handle mail connection information caching.
+mime.c MIME conversion routines.
+parseaddr.c The routines which do address parsing.
+queue.c Routines to implement message queueing.
+readcf.c The routine that reads the configuration file and
+ translates it to internal form.
+recipient.c Routines that manipulate the recipient list.
+safefile.c Routines to do careful checking of file modes and permissions
+ when opening or creating files.
+savemail.c Routines which save the letter on processing errors.
+sendmail.h Main header file for sendmail.
+snprintf.c Routines to manipulate strings but prevent buffer overflows.
+srvrsmtp.c Routines to implement server SMTP.
+stab.c Routines to manage the symbol table.
+stats.c Routines to collect and post the statistics.
+sysexits.c List of error messages associated with error codes
+ in sysexits.h.
+trace.c The trace package. These routines allow setting and
+ testing of trace flags with a high granularity.
+udb.c The user database interface module.
+usersmtp.c Routines to implement user SMTP.
+util.c Some general purpose routines used by sendmail.
+version.c The version number and information about this
+ version of sendmail. Theoretically, this gets
+ modified on every change.
+
+Eric Allman
+
+(Version 8.206, last update 6/30/98 22:08:36)
diff --git a/contrib/sendmail/src/TRACEFLAGS b/contrib/sendmail/src/TRACEFLAGS
new file mode 100644
index 0000000..588714d
--- /dev/null
+++ b/contrib/sendmail/src/TRACEFLAGS
@@ -0,0 +1,79 @@
+# @(#)TRACEFLAGS 8.21 (Berkeley) 4/27/98
+0, 1 main.c main skip background fork
+0, 4 main.c main canonical name, UUCP node name, a.k.a.s
+0, 15 main.c main print configuration
+0, 44 util.c printav print address of each string
+0, 101 main.c main print version and exit
+1 main.c main print from person
+2 main.c finis
+3 conf.c getla, shouldqueue
+4 conf.c enoughspace
+5 clock.c setevent, clrevent, tick
+6 savemail.c savemail, returntosender
+7 queue.c queuename
+8 domain.c getmxrr, getcanonname
+9 daemon.c getauthinfo IDENT protocol
+9 daemon.c maphostname
+10 deliver.c deliver
+11 deliver.c openmailer, mailfile
+12 parseaddr.c remotename
+13 deliver.c sendall, sendenvelope
+14 headers.c commaize
+15 daemon.c getrequests
+16 daemon.c makeconnection
+17 deliver.c hostsignature
+17 domain.c mxrand
+18 usersmtp.c reply, smtpmessage, smtpinit, smtpmailfrom
+19 srvrsmtp.c smtp
+20 parseaddr.c parseaddr
+21 parseaddr.c rewrite
+22 parseaddr.c prescan
+24 parseaddr.c buildaddr, allocaddr
+25 recipient.c sendtolist
+26 recipient.c recipient
+27 alias.c alias
+27 alias.c readaliases
+27 alias.c forward
+27 recipient.c include
+28 udb.c udbexpand, udbsender
+29 parseaddr.c maplocaluser
+29 recipient.c recipient (local users), finduser
+30 collect.c collect
+30 collect.c eatfrom
+31 headers.c chompheader
+32 headers.c eatheader
+33 headers.c crackaddr
+34 headers.c putheader
+35 macro.c expand, define
+36 stab.c stab
+37 readcf.c (many)
+38 map.c initmaps
+39 map.c map_rewrite
+40 queue.c queueup, orderq, dowork
+41 queue.c orderq
+42 mci.c mci_get
+43 mime.c mime8to7
+44 recipient.c writeable
+44 safefile.c safefile, safedirpath, filechanged
+45 envelope.c setsender
+46 envelope.c openxscript
+47 main.c drop_privileges
+48 parseaddr.c rscheck
+48 conf.c validate_connection
+49 conf.c checkcompat
+50 envelope.c dropenvelope
+51 queue.c unlockqueue
+52 main.c disconnect
+53 util.c xfclose
+54 err.c putoutmsg
+55 conf.c lockfile
+56 mci.c persistent host status
+57 util.c snprintf
+60 map.c
+61 conf.c sm_gethostbyname
+62 multiple file descriptor checking
+80 content length
+81 sun remote mode
+91 mci.c syslogging of MCI cache information
+94 srvrsmtp.c cause commands to fail (for protocol testing)
+99 main.c avoid backgrounding (no printed output)
diff --git a/contrib/sendmail/src/alias.c b/contrib/sendmail/src/alias.c
new file mode 100644
index 0000000..8da3317
--- /dev/null
+++ b/contrib/sendmail/src/alias.c
@@ -0,0 +1,894 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. 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.
+ *
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ */
+
+# include "sendmail.h"
+
+#ifndef lint
+static char sccsid[] = "@(#)alias.c 8.92 (Berkeley) 6/5/98";
+#endif /* not lint */
+
+
+MAP *AliasFileMap = NULL; /* the actual aliases.files map */
+int NAliasFileMaps; /* the number of entries in AliasFileMap */
+ /*
+** ALIAS -- Compute aliases.
+**
+** Scans the alias file for an alias for the given address.
+** If found, it arranges to deliver to the alias list instead.
+** Uses libdbm database if -DDBM.
+**
+** Parameters:
+** a -- address to alias.
+** sendq -- a pointer to the head of the send queue
+** to put the aliases in.
+** aliaslevel -- the current alias nesting depth.
+** e -- the current envelope.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Aliases found are expanded.
+**
+** Deficiencies:
+** It should complain about names that are aliased to
+** nothing.
+*/
+
+void
+alias(a, sendq, aliaslevel, e)
+ register ADDRESS *a;
+ ADDRESS **sendq;
+ int aliaslevel;
+ register ENVELOPE *e;
+{
+ register char *p;
+ char *owner;
+ auto int stat = EX_OK;
+ char obuf[MAXNAME + 7];
+ extern char *aliaslookup __P((char *, int *, ENVELOPE *));
+
+ if (tTd(27, 1))
+ printf("alias(%s)\n", a->q_user);
+
+ /* don't realias already aliased names */
+ if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
+ return;
+
+ if (NoAlias)
+ return;
+
+ e->e_to = a->q_paddr;
+
+ /*
+ ** Look up this name.
+ **
+ ** If the map was unavailable, we will queue this message
+ ** until the map becomes available; otherwise, we could
+ ** bounce messages inappropriately.
+ */
+
+ p = aliaslookup(a->q_user, &stat, e);
+ if (stat == EX_TEMPFAIL || stat == EX_UNAVAILABLE)
+ {
+ a->q_flags |= QQUEUEUP;
+ if (e->e_message == NULL)
+ e->e_message = newstr("alias database unavailable");
+ return;
+ }
+ if (p == NULL)
+ return;
+
+ /*
+ ** Match on Alias.
+ ** Deliver to the target list.
+ */
+
+ if (tTd(27, 1))
+ printf("%s (%s, %s) aliased to %s\n",
+ a->q_paddr, a->q_host, a->q_user, p);
+ if (bitset(EF_VRFYONLY, e->e_flags))
+ {
+ a->q_flags |= QVERIFIED;
+ return;
+ }
+ message("aliased to %s", shortenstring(p, MAXSHORTSTR));
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id,
+ "alias %.100s => %s",
+ a->q_paddr, shortenstring(p, MAXSHORTSTR));
+ a->q_flags &= ~QSELFREF;
+ if (tTd(27, 5))
+ {
+ printf("alias: QDONTSEND ");
+ printaddr(a, FALSE);
+ }
+ a->q_flags |= QDONTSEND;
+ (void) sendtolist(p, a, sendq, aliaslevel + 1, e);
+ if (bitset(QSELFREF, a->q_flags))
+ a->q_flags &= ~QDONTSEND;
+
+ /*
+ ** Look for owner of alias
+ */
+
+ (void) strcpy(obuf, "owner-");
+ if (strncmp(a->q_user, "owner-", 6) == 0 ||
+ strlen(a->q_user) > (SIZE_T) sizeof obuf - 7)
+ (void) strcat(obuf, "owner");
+ else
+ (void) strcat(obuf, a->q_user);
+ owner = aliaslookup(obuf, &stat, e);
+ if (owner == NULL)
+ return;
+
+ /* reflect owner into envelope sender */
+ if (strpbrk(owner, ",:/|\"") != NULL)
+ owner = obuf;
+ a->q_owner = newstr(owner);
+
+ /* announce delivery to this alias; NORECEIPT bit set later */
+ if (e->e_xfp != NULL)
+ fprintf(e->e_xfp, "Message delivered to mailing list %s\n",
+ a->q_paddr);
+ e->e_flags |= EF_SENDRECEIPT;
+ a->q_flags |= QDELIVERED|QEXPANDED;
+}
+ /*
+** ALIASLOOKUP -- look up a name in the alias file.
+**
+** Parameters:
+** name -- the name to look up.
+** pstat -- a pointer to a place to put the status.
+** e -- the current envelope.
+**
+** Returns:
+** the value of name.
+** NULL if unknown.
+**
+** Side Effects:
+** none.
+**
+** Warnings:
+** The return value will be trashed across calls.
+*/
+
+char *
+aliaslookup(name, pstat, e)
+ char *name;
+ int *pstat;
+ ENVELOPE *e;
+{
+ static MAP *map = NULL;
+
+ if (map == NULL)
+ {
+ STAB *s = stab("aliases", ST_MAP, ST_FIND);
+
+ if (s == NULL)
+ return NULL;
+ map = &s->s_map;
+ }
+ if (!bitset(MF_OPEN, map->map_mflags))
+ return NULL;
+
+ /* special case POstMastER -- always use lower case */
+ if (strcasecmp(name, "postmaster") == 0)
+ name = "postmaster";
+
+ return (*map->map_class->map_lookup)(map, name, NULL, pstat);
+}
+ /*
+** SETALIAS -- set up an alias map
+**
+** Called when reading configuration file.
+**
+** Parameters:
+** spec -- the alias specification
+**
+** Returns:
+** none.
+*/
+
+void
+setalias(spec)
+ char *spec;
+{
+ register char *p;
+ register MAP *map;
+ char *class;
+ STAB *s;
+
+ if (tTd(27, 8))
+ printf("setalias(%s)\n", spec);
+
+ for (p = spec; p != NULL; )
+ {
+ char buf[50];
+
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ spec = p;
+
+ if (NAliasFileMaps >= MAXMAPSTACK)
+ {
+ syserr("Too many alias databases defined, %d max",
+ MAXMAPSTACK);
+ return;
+ }
+ if (AliasFileMap == NULL)
+ {
+ strcpy(buf, "aliases.files sequence");
+ AliasFileMap = makemapentry(buf);
+ if (AliasFileMap == NULL)
+ {
+ syserr("setalias: cannot create aliases.files map");
+ return;
+ }
+ }
+ (void) snprintf(buf, sizeof buf, "Alias%d", NAliasFileMaps);
+ s = stab(buf, ST_MAP, ST_ENTER);
+ map = &s->s_map;
+ bzero(map, sizeof *map);
+ map->map_mname = s->s_name;
+
+ p = strpbrk(p, " ,/:");
+ if (p != NULL && *p == ':')
+ {
+ /* map name */
+ *p++ = '\0';
+ class = spec;
+ spec = p;
+ }
+ else
+ {
+ class = "implicit";
+ map->map_mflags = MF_INCLNULL;
+ }
+
+ /* find end of spec */
+ if (p != NULL)
+ p = strchr(p, ',');
+ if (p != NULL)
+ *p++ = '\0';
+
+ if (tTd(27, 20))
+ printf(" map %s:%s %s\n", class, s->s_name, spec);
+
+ /* look up class */
+ s = stab(class, ST_MAPCLASS, ST_FIND);
+ if (s == NULL)
+ {
+ syserr("setalias: unknown alias class %s", class);
+ }
+ else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
+ {
+ syserr("setalias: map class %s can't handle aliases",
+ class);
+ }
+ else
+ {
+ map->map_class = &s->s_mapclass;
+ if (map->map_class->map_parse(map, spec))
+ {
+ map->map_mflags |= MF_VALID|MF_ALIAS;
+ AliasFileMap->map_stack[NAliasFileMaps++] = map;
+ }
+ }
+ }
+}
+ /*
+** ALIASWAIT -- wait for distinguished @:@ token to appear.
+**
+** This can decide to reopen or rebuild the alias file
+**
+** Parameters:
+** map -- a pointer to the map descriptor for this alias file.
+** ext -- the filename extension (e.g., ".db") for the
+** database file.
+** isopen -- if set, the database is already open, and we
+** should check for validity; otherwise, we are
+** just checking to see if it should be created.
+**
+** Returns:
+** TRUE -- if the database is open when we return.
+** FALSE -- if the database is closed when we return.
+*/
+
+bool
+aliaswait(map, ext, isopen)
+ MAP *map;
+ char *ext;
+ int isopen;
+{
+ bool attimeout = FALSE;
+ time_t mtime;
+ struct stat stb;
+ char buf[MAXNAME + 1];
+
+ if (tTd(27, 3))
+ printf("aliaswait(%s:%s)\n",
+ map->map_class->map_cname, map->map_file);
+ if (bitset(MF_ALIASWAIT, map->map_mflags))
+ return isopen;
+ map->map_mflags |= MF_ALIASWAIT;
+
+ if (SafeAlias > 0)
+ {
+ auto int st;
+ time_t toolong = curtime() + SafeAlias;
+ unsigned int sleeptime = 2;
+
+ while (isopen &&
+ map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
+ {
+ if (curtime() > toolong)
+ {
+ /* we timed out */
+ attimeout = TRUE;
+ break;
+ }
+
+ /*
+ ** Close and re-open the alias database in case
+ ** the one is mv'ed instead of cp'ed in.
+ */
+
+ if (tTd(27, 2))
+ printf("aliaswait: sleeping for %d seconds\n",
+ sleeptime);
+
+ map->map_class->map_close(map);
+ map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+ sleep(sleeptime);
+ sleeptime *= 2;
+ if (sleeptime > 60)
+ sleeptime = 60;
+ isopen = map->map_class->map_open(map, O_RDONLY);
+ }
+ }
+
+ /* see if we need to go into auto-rebuild mode */
+ if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
+ {
+ if (tTd(27, 3))
+ printf("aliaswait: not rebuildable\n");
+ map->map_mflags &= ~MF_ALIASWAIT;
+ return isopen;
+ }
+ if (stat(map->map_file, &stb) < 0)
+ {
+ if (tTd(27, 3))
+ printf("aliaswait: no source file\n");
+ map->map_mflags &= ~MF_ALIASWAIT;
+ return isopen;
+ }
+ mtime = stb.st_mtime;
+ snprintf(buf, sizeof buf, "%s%s",
+ map->map_file, ext == NULL ? "" : ext);
+ if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
+ {
+ /* database is out of date */
+ if (AutoRebuild && stb.st_ino != 0 &&
+ (stb.st_uid == geteuid() ||
+ (geteuid() == 0 && stb.st_uid == TrustedFileUid)))
+ {
+ bool oldSuprErrs;
+
+ message("auto-rebuilding alias database %s", buf);
+ oldSuprErrs = SuprErrs;
+ SuprErrs = TRUE;
+ if (isopen)
+ {
+ map->map_class->map_close(map);
+ map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+ }
+ (void) rebuildaliases(map, TRUE);
+ isopen = map->map_class->map_open(map, O_RDONLY);
+ SuprErrs = oldSuprErrs;
+ }
+ else
+ {
+ if (LogLevel > 3)
+ sm_syslog(LOG_INFO, NOQID,
+ "alias database %s out of date",
+ buf);
+ message("Warning: alias database %s out of date", buf);
+ }
+ }
+ map->map_mflags &= ~MF_ALIASWAIT;
+ return isopen;
+}
+ /*
+** REBUILDALIASES -- rebuild the alias database.
+**
+** Parameters:
+** map -- the database to rebuild.
+** automatic -- set if this was automatically generated.
+**
+** Returns:
+** TRUE if successful; FALSE otherwise.
+**
+** Side Effects:
+** Reads the text version of the database, builds the
+** DBM or DB version.
+*/
+
+bool
+rebuildaliases(map, automatic)
+ register MAP *map;
+ bool automatic;
+{
+ FILE *af;
+ bool nolock = FALSE;
+ bool success = FALSE;
+ int sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
+ sigfunc_t oldsigint, oldsigquit;
+#ifdef SIGTSTP
+ sigfunc_t oldsigtstp;
+#endif
+
+ if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
+ return FALSE;
+
+ if (!bitset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
+ sff |= SFF_NOWLINK;
+ if (!bitset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
+ sff |= SFF_NOGWFILES;
+ if (!bitset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
+ sff |= SFF_NOWWFILES;
+
+ /* try to lock the source file */
+ if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL)
+ {
+ struct stat stb;
+
+ if ((errno != EACCES && errno != EROFS) || automatic ||
+ (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL)
+ {
+ int saveerr = errno;
+
+ if (tTd(27, 1))
+ printf("Can't open %s: %s\n",
+ map->map_file, errstring(saveerr));
+ if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
+ message("newaliases: cannot open %s: %s",
+ map->map_file, errstring(saveerr));
+ errno = 0;
+ return FALSE;
+ }
+ nolock = TRUE;
+ if (tTd(27, 1) ||
+ fstat(fileno(af), &stb) < 0 ||
+ bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
+ message("warning: cannot lock %s: %s",
+ map->map_file, errstring(errno));
+ }
+
+ /* see if someone else is rebuilding the alias file */
+ if (!nolock &&
+ !lockfile(fileno(af), map->map_file, NULL, LOCK_EX|LOCK_NB))
+ {
+ /* yes, they are -- wait until done */
+ message("Alias file %s is locked (maybe being rebuilt)",
+ map->map_file);
+ if (OpMode != MD_INITALIAS)
+ {
+ /* wait for other rebuild to complete */
+ (void) lockfile(fileno(af), map->map_file, NULL,
+ LOCK_EX);
+ }
+ (void) xfclose(af, "rebuildaliases1", map->map_file);
+ errno = 0;
+ return FALSE;
+ }
+
+ oldsigint = setsignal(SIGINT, SIG_IGN);
+ oldsigquit = setsignal(SIGQUIT, SIG_IGN);
+#ifdef SIGTSTP
+ oldsigtstp = setsignal(SIGTSTP, SIG_IGN);
+#endif
+
+ if (map->map_class->map_open(map, O_RDWR))
+ {
+ if (LogLevel > 7)
+ {
+ sm_syslog(LOG_NOTICE, NOQID,
+ "alias database %s %srebuilt by %s",
+ map->map_file, automatic ? "auto" : "",
+ username());
+ }
+ map->map_mflags |= MF_OPEN|MF_WRITABLE;
+ readaliases(map, af, !automatic, TRUE);
+ success = TRUE;
+ }
+ else
+ {
+ if (tTd(27, 1))
+ printf("Can't create database for %s: %s\n",
+ map->map_file, errstring(errno));
+ if (!automatic)
+ syserr("Cannot create database for alias file %s",
+ map->map_file);
+ }
+
+ /* close the file, thus releasing locks */
+ xfclose(af, "rebuildaliases2", map->map_file);
+
+ /* add distinguished entries and close the database */
+ if (bitset(MF_OPEN, map->map_mflags))
+ {
+ map->map_class->map_close(map);
+ map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+ }
+
+ /* restore the old signals */
+ (void) setsignal(SIGINT, oldsigint);
+ (void) setsignal(SIGQUIT, oldsigquit);
+#ifdef SIGTSTP
+ (void) setsignal(SIGTSTP, oldsigtstp);
+#endif
+ return success;
+}
+ /*
+** READALIASES -- read and process the alias file.
+**
+** This routine implements the part of initaliases that occurs
+** when we are not going to use the DBM stuff.
+**
+** Parameters:
+** map -- the alias database descriptor.
+** af -- file to read the aliases from.
+** announcestats -- anounce statistics regarding number of
+** aliases, longest alias, etc.
+** logstats -- lot the same info.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Reads aliasfile into the symbol table.
+** Optionally, builds the .dir & .pag files.
+*/
+
+void
+readaliases(map, af, announcestats, logstats)
+ register MAP *map;
+ FILE *af;
+ bool announcestats;
+ bool logstats;
+{
+ register char *p;
+ char *rhs;
+ bool skipping;
+ long naliases, bytes, longest;
+ ADDRESS al, bl;
+ char line[BUFSIZ];
+
+ /*
+ ** Read and interpret lines
+ */
+
+ FileName = map->map_file;
+ LineNumber = 0;
+ naliases = bytes = longest = 0;
+ skipping = FALSE;
+ while (fgets(line, sizeof (line), af) != NULL)
+ {
+ int lhssize, rhssize;
+ int c;
+
+ LineNumber++;
+ p = strchr(line, '\n');
+#if _FFR_BACKSLASH_IN_ALIASES
+ while (p != NULL && p > line && p[-1] == '\\')
+ {
+ p--;
+ if (fgets(p, SPACELEFT(line, p), af) == NULL)
+ break;
+ LineNumber++;
+ p = strchr(p, '\n');
+ }
+#endif
+ if (p != NULL)
+ *p = '\0';
+ else if (!feof(af))
+ {
+ syserr("554 alias line too long");
+
+ /* flush to end of line */
+ while ((c = getc(af)) != EOF && c != '\n')
+ continue;
+
+ /* skip any continuation lines */
+ skipping = TRUE;
+ continue;
+ }
+ switch (line[0])
+ {
+ case '#':
+ case '\0':
+ skipping = FALSE;
+ continue;
+
+ case ' ':
+ case '\t':
+ if (!skipping)
+ syserr("554 Non-continuation line starts with space");
+ skipping = TRUE;
+ continue;
+ }
+ skipping = FALSE;
+
+ /*
+ ** Process the LHS
+ ** Find the colon separator, and parse the address.
+ ** It should resolve to a local name -- this will
+ ** be checked later (we want to optionally do
+ ** parsing of the RHS first to maximize error
+ ** detection).
+ */
+
+ for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
+ continue;
+ if (*p++ != ':')
+ {
+ syserr("554 missing colon");
+ continue;
+ }
+ if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL)
+ {
+ syserr("554 %.40s... illegal alias name", line);
+ continue;
+ }
+
+ /*
+ ** Process the RHS.
+ ** 'al' is the internal form of the LHS address.
+ ** 'p' points to the text of the RHS.
+ */
+
+ while (isascii(*p) && isspace(*p))
+ p++;
+ rhs = p;
+ for (;;)
+ {
+ register char *nlp;
+
+ nlp = &p[strlen(p)];
+ if (nlp[-1] == '\n')
+ *--nlp = '\0';
+
+ if (CheckAliases)
+ {
+ /* do parsing & compression of addresses */
+ while (*p != '\0')
+ {
+ auto char *delimptr;
+
+ while ((isascii(*p) && isspace(*p)) ||
+ *p == ',')
+ p++;
+ if (*p == '\0')
+ break;
+ if (parseaddr(p, &bl, RF_COPYNONE, ',',
+ &delimptr, CurEnv) == NULL)
+ usrerr("553 %s... bad address", p);
+ p = delimptr;
+ }
+ }
+ else
+ {
+ p = nlp;
+ }
+
+ /* see if there should be a continuation line */
+ c = getc(af);
+ if (!feof(af))
+ (void) ungetc(c, af);
+ if (c != ' ' && c != '\t')
+ break;
+
+ /* read continuation line */
+ if (fgets(p, sizeof line - (p - line), af) == NULL)
+ break;
+ LineNumber++;
+
+ /* check for line overflow */
+ if (strchr(p, '\n') == NULL && !feof(af))
+ {
+ usrerr("554 alias too long");
+ while ((c = fgetc(af)) != EOF && c != '\n')
+ continue;
+ skipping = TRUE;
+ break;
+ }
+ }
+
+ if (skipping)
+ continue;
+
+ if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
+ {
+ syserr("554 %s... cannot alias non-local names",
+ al.q_paddr);
+ continue;
+ }
+
+ /*
+ ** Insert alias into symbol table or database file.
+ **
+ ** Special case pOStmaStER -- always make it lower case.
+ */
+
+ if (strcasecmp(al.q_user, "postmaster") == 0)
+ makelower(al.q_user);
+
+ lhssize = strlen(al.q_user);
+ rhssize = strlen(rhs);
+ map->map_class->map_store(map, al.q_user, rhs);
+
+ if (al.q_paddr != NULL)
+ free(al.q_paddr);
+ if (al.q_host != NULL)
+ free(al.q_host);
+ if (al.q_user != NULL)
+ free(al.q_user);
+
+ /* statistics */
+ naliases++;
+ bytes += lhssize + rhssize;
+ if (rhssize > longest)
+ longest = rhssize;
+ }
+
+ CurEnv->e_to = NULL;
+ FileName = NULL;
+ if (Verbose || announcestats)
+ message("%s: %d aliases, longest %d bytes, %d bytes total",
+ map->map_file, naliases, longest, bytes);
+ if (LogLevel > 7 && logstats)
+ sm_syslog(LOG_INFO, NOQID,
+ "%s: %d aliases, longest %d bytes, %d bytes total",
+ map->map_file, naliases, longest, bytes);
+}
+ /*
+** FORWARD -- Try to forward mail
+**
+** This is similar but not identical to aliasing.
+**
+** Parameters:
+** user -- the name of the user who's mail we would like
+** to forward to. It must have been verified --
+** i.e., the q_home field must have been filled
+** in.
+** sendq -- a pointer to the head of the send queue to
+** put this user's aliases in.
+** aliaslevel -- the current alias nesting depth.
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** New names are added to send queues.
+*/
+
+void
+forward(user, sendq, aliaslevel, e)
+ ADDRESS *user;
+ ADDRESS **sendq;
+ int aliaslevel;
+ register ENVELOPE *e;
+{
+ char *pp;
+ char *ep;
+ bool got_transient;
+
+ if (tTd(27, 1))
+ printf("forward(%s)\n", user->q_paddr);
+
+ if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
+ bitset(QBADADDR, user->q_flags))
+ return;
+ if (user->q_home == NULL)
+ {
+ syserr("554 forward: no home");
+ user->q_home = "/no/such/directory";
+ }
+
+ /* good address -- look for .forward file in home */
+ define('z', user->q_home, e);
+ define('u', user->q_user, e);
+ define('h', user->q_host, e);
+ if (ForwardPath == NULL)
+ ForwardPath = newstr("\201z/.forward");
+
+ got_transient = FALSE;
+ for (pp = ForwardPath; pp != NULL; pp = ep)
+ {
+ int err;
+ char buf[MAXPATHLEN+1];
+
+ ep = strchr(pp, ':');
+ if (ep != NULL)
+ *ep = '\0';
+ expand(pp, buf, sizeof buf, e);
+ if (ep != NULL)
+ *ep++ = ':';
+ if (buf[0] == '\0')
+ continue;
+ if (tTd(27, 3))
+ printf("forward: trying %s\n", buf);
+
+ err = include(buf, TRUE, user, sendq, aliaslevel, e);
+ if (err == 0)
+ break;
+ else if (transienterror(err))
+ {
+ /* we may have to suspend this message */
+ got_transient = TRUE;
+ if (tTd(27, 2))
+ printf("forward: transient error on %s\n", buf);
+ if (LogLevel > 2)
+ sm_syslog(LOG_ERR, e->e_id,
+ "forward %s: transient error: %s",
+ buf, errstring(err));
+ }
+ else
+ {
+ switch (err)
+ {
+ case ENOENT:
+ break;
+
+#if _FFR_FORWARD_SYSERR
+ case E_SM_NOSLINK:
+ case E_SM_NOHLINK:
+ case E_SM_REGONLY:
+ case E_SM_ISEXEC:
+ case E_SM_WWDIR:
+ case E_SM_GWDIR:
+ case E_SM_WWFILE:
+ case E_SM_GWFILE:
+ syserr("forward: %s: %s", buf, errstring(err));
+ break;
+#endif
+
+ default:
+ if (LogLevel > (RunAsUid == 0 ? 2 : 10))
+ sm_syslog(LOG_WARNING, e->e_id,
+ "forward %s: %s", buf,
+ errstring(err));
+ if (Verbose)
+ message("forward: %s: %s",
+ buf,
+ errstring(err));
+ break;
+ }
+ }
+ }
+ if (pp == NULL && got_transient)
+ {
+ /*
+ ** There was no successful .forward open and at least one
+ ** transient open. We have to defer this address for
+ ** further delivery.
+ */
+
+ message("transient .forward open error: message queued");
+ user->q_flags |= QQUEUEUP;
+ return;
+ }
+}
diff --git a/contrib/sendmail/src/aliases b/contrib/sendmail/src/aliases
new file mode 100644
index 0000000..7540eea
--- /dev/null
+++ b/contrib/sendmail/src/aliases
@@ -0,0 +1,53 @@
+#
+# @(#)aliases 8.2 (Berkeley) 3/5/94
+#
+# Aliases in this file will NOT be expanded in the header from
+# Mail, but WILL be visible over networks or from /bin/mail.
+#
+# >>>>>>>>>> The program "newaliases" must be run after
+# >> NOTE >> this file is updated for any changes to
+# >>>>>>>>>> show through to sendmail.
+#
+
+# Basic system aliases -- these MUST be present.
+MAILER-DAEMON: postmaster
+postmaster: root
+
+# General redirections for pseudo accounts.
+bin: root
+daemon: root
+games: root
+ingres: root
+nobody: root
+system: root
+toor: root
+uucp: root
+
+# Well-known aliases.
+manager: root
+dumper: root
+operator: root
+
+# trap decode to catch security attacks
+decode: root
+
+# OFFICIAL CSRG/BUG ADDRESSES
+
+# Ftp maintainer.
+ftp: ftp-bugs
+ftp-bugs: bigbug@cs.berkeley.edu
+
+# Distribution office.
+bsd-dist: bsd-dist@cs.berkeley.edu
+
+# Fortune maintainer.
+fortune: fortune@cs.berkeley.edu
+
+# Termcap maintainer.
+termcap: termcap@cs.berkeley.edu
+
+# General bug address.
+ucb-fixes: bigbug@cs.berkeley.edu
+ucb-fixes-request: bigbug@cs.berkeley.edu
+bugs: bugs@cs.berkeley.edu
+# END OFFICIAL BUG ADDRESSES
diff --git a/contrib/sendmail/src/aliases.5 b/contrib/sendmail/src/aliases.5
new file mode 100644
index 0000000..8c73ac3
--- /dev/null
+++ b/contrib/sendmail/src/aliases.5
@@ -0,0 +1,85 @@
+.\" Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+.\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved.
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. 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.
+.\"
+.\"
+.\" @(#)aliases.5 8.8 (Berkeley) 5/19/98
+.\"
+.Dd May 19, 1998
+.Dt ALIASES 5
+.Os BSD 4
+.Sh NAME
+.Nm aliases
+.Nd aliases file for sendmail
+.Sh SYNOPSIS
+.Nm aliases
+.Sh DESCRIPTION
+This file describes user
+.Tn ID
+aliases used by
+.Pa /usr/sbin/sendmail .
+The file resides in
+.Pa /etc
+and
+is formatted as a series of lines of the form
+.Bd -filled -offset indent
+name: name_1, name2, name_3, . . .
+.Ed
+.Pp
+The
+.Em name
+is the name to alias, and the
+.Em name_n
+are the aliases for that name.
+Lines beginning with white space are continuation lines.
+Lines beginning with
+.Ql #
+are comments.
+.Pp
+Aliasing occurs only on local names.
+Loops can not occur, since no message will be sent to any person more than once.
+.Pp
+After aliasing has been done, local and valid recipients who have a
+.Dq Pa .forward
+file in their home directory have messages forwarded to the
+list of users defined in that file.
+.Pp
+This is only the raw data file; the actual aliasing information is
+placed into a binary format in the file
+.Pa /etc/aliases.db
+using the program
+.Xr newaliases 1 .
+A
+.Xr newaliases
+command should be executed each time the aliases file is changed for the
+change to take effect.
+.Sh SEE ALSO
+.Xr newaliases 1 ,
+.Xr dbopen 3 ,
+.Xr dbm 3 ,
+.Xr sendmail 8
+.Rs
+.%T "SENDMAIL Installation and Operation Guide"
+.Re
+.Rs
+.%T "SENDMAIL An Internetwork Mail Router"
+.Re
+.Sh BUGS
+If you have compiled
+.Xr sendmail
+with DBM support instead of NEWDB,
+you may have encountered problems in
+.Xr dbm 3
+restricting a single alias to about 1000 bytes of information.
+You can get longer aliases by ``chaining''; that is, make the last name in
+the alias be a dummy name which is a continuation alias.
+.Sh HISTORY
+The
+.Nm
+file format appeared in
+.Bx 4.0 .
diff --git a/contrib/sendmail/src/arpadate.c b/contrib/sendmail/src/arpadate.c
new file mode 100644
index 0000000..7a9576b
--- /dev/null
+++ b/contrib/sendmail/src/arpadate.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)arpadate.c 8.12 (Berkeley) 5/19/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+** ARPADATE -- Create date in ARPANET format
+**
+** Parameters:
+** ud -- unix style date string. if NULL, one is created.
+**
+** Returns:
+** pointer to an ARPANET date field
+**
+** Side Effects:
+** none
+**
+** WARNING:
+** date is stored in a local buffer -- subsequent
+** calls will overwrite.
+**
+** Bugs:
+** Timezone is computed from local time, rather than
+** from whereever (and whenever) the message was sent.
+** To do better is very hard.
+**
+** Some sites are now inserting the timezone into the
+** local date. This routine should figure out what
+** the format is and work appropriately.
+*/
+
+#ifndef TZNAME_MAX
+# define TZNAME_MAX 50 /* max size of timezone */
+#endif
+
+/* values for TZ_TYPE */
+#define TZ_NONE 0 /* no character timezone support */
+#define TZ_TM_NAME 1 /* use tm->tm_name */
+#define TZ_TM_ZONE 2 /* use tm->tm_zone */
+#define TZ_TZNAME 3 /* use tzname[] */
+#define TZ_TIMEZONE 4 /* use timezone() */
+
+char *
+arpadate(ud)
+ register char *ud;
+{
+ register char *p;
+ register char *q;
+ register int off;
+ register int i;
+ register struct tm *lt;
+ time_t t;
+ struct tm gmt;
+ char *tz;
+ static char b[43 + TZNAME_MAX];
+
+ /*
+ ** Get current time.
+ ** This will be used if a null argument is passed and
+ ** to resolve the timezone.
+ */
+
+ (void) time(&t);
+ if (ud == NULL)
+ ud = ctime(&t);
+
+ /*
+ ** Crack the UNIX date line in a singularly unoriginal way.
+ */
+
+ q = b;
+
+ p = &ud[0]; /* Mon */
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = ',';
+ *q++ = ' ';
+
+ p = &ud[8]; /* 16 */
+ if (*p == ' ')
+ p++;
+ else
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = ' ';
+
+ p = &ud[4]; /* Sep */
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = ' ';
+
+ p = &ud[20]; /* 1979 */
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = ' ';
+
+ p = &ud[11]; /* 01:03:52 */
+ for (i = 8; i > 0; i--)
+ *q++ = *p++;
+
+ /*
+ * should really get the timezone from the time in "ud" (which
+ * is only different if a non-null arg was passed which is different
+ * from the current time), but for all practical purposes, returning
+ * the current local zone will do (its all that is ever needed).
+ */
+ gmt = *gmtime(&t);
+ lt = localtime(&t);
+
+ off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
+
+ /* assume that offset isn't more than a day ... */
+ if (lt->tm_year < gmt.tm_year)
+ off -= 24 * 60;
+ else if (lt->tm_year > gmt.tm_year)
+ off += 24 * 60;
+ else if (lt->tm_yday < gmt.tm_yday)
+ off -= 24 * 60;
+ else if (lt->tm_yday > gmt.tm_yday)
+ off += 24 * 60;
+
+ *q++ = ' ';
+ if (off == 0)
+ {
+ *q++ = 'G';
+ *q++ = 'M';
+ *q++ = 'T';
+ }
+ else
+ {
+ tz = NULL;
+#if TZ_TYPE == TZ_TM_NAME
+ tz = lt->tm_name;
+#endif
+#if TZ_TYPE == TZ_TM_ZONE
+ tz = lt->tm_zone;
+#endif
+#if TZ_TYPE == TZ_TZNAME
+ {
+ extern char *tzname[];
+
+ tz = tzname[lt->tm_isdst];
+ }
+#endif
+#if TZ_TYPE == TZ_TIMEZONE
+ {
+ extern char *timezone();
+
+ tz = timezone(off, lt->tm_isdst);
+ }
+#endif
+ if (off < 0)
+ {
+ off = -off;
+ *q++ = '-';
+ }
+ else
+ *q++ = '+';
+
+ if (off >= 24*60) /* should be impossible */
+ off = 23*60+59; /* if not, insert silly value */
+
+ *q++ = (off / 600) + '0';
+ *q++ = (off / 60) % 10 + '0';
+ off %= 60;
+ *q++ = (off / 10) + '0';
+ *q++ = (off % 10) + '0';
+ if (tz != NULL && *tz != '\0')
+ {
+ *q++ = ' ';
+ *q++ = '(';
+ while (*tz != '\0' && q < &b[sizeof b - 3])
+ *q++ = *tz++;
+ *q++ = ')';
+ }
+ }
+ *q = '\0';
+
+ return (b);
+}
diff --git a/contrib/sendmail/src/cdefs.h b/contrib/sendmail/src/cdefs.h
new file mode 100644
index 0000000..e586cbf
--- /dev/null
+++ b/contrib/sendmail/src/cdefs.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ *
+ * @(#)cdefs.h 8.8 (Berkeley) 1/9/95
+ */
+
+#ifndef _CDEFS_H_
+#define _CDEFS_H_
+
+#if defined(__cplusplus)
+#define __BEGIN_DECLS extern "C" {
+#define __END_DECLS };
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments. __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus)
+#define __P(protos) protos /* full-blown ANSI C */
+#define __CONCAT(x,y) x ## y
+#define __STRING(x) #x
+
+#define __const const /* define reserved names to standard */
+#define __signed signed
+#define __volatile volatile
+#if defined(__cplusplus)
+#define __inline inline /* convert to C++ keyword */
+#else
+#ifndef __GNUC__
+#define __inline /* delete GCC keyword */
+#endif /* !__GNUC__ */
+#endif /* !__cplusplus */
+
+#else /* !(__STDC__ || __cplusplus) */
+#define __P(protos) () /* traditional C preprocessor */
+#define __CONCAT(x,y) x/**/y
+#define __STRING(x) "x"
+
+#ifndef __GNUC__
+#define __const /* delete pseudo-ANSI C keywords */
+#define __inline
+#define __signed
+#define __volatile
+/*
+ * In non-ANSI C environments, new programs will want ANSI-only C keywords
+ * deleted from the program and old programs will want them left alone.
+ * When using a compiler other than gcc, programs using the ANSI C keywords
+ * const, inline etc. as normal identifiers should define -DNO_ANSI_KEYWORDS.
+ * When using "gcc -traditional", we assume that this is the intent; if
+ * __GNUC__ is defined but __STDC__ is not, we leave the new keywords alone.
+ */
+#ifndef NO_ANSI_KEYWORDS
+#define const /* delete ANSI C keywords */
+#define inline
+#define signed
+#define volatile
+#endif
+#endif /* !__GNUC__ */
+#endif /* !(__STDC__ || __cplusplus) */
+
+/*
+ * GCC1 and some versions of GCC2 declare dead (non-returning) and
+ * pure (no side effects) functions using "volatile" and "const";
+ * unfortunately, these then cause warnings under "-ansi -pedantic".
+ * GCC2 uses a new, peculiar __attribute__((attrs)) style. All of
+ * these work for GNU C++ (modulo a slight glitch in the C++ grammar
+ * in the distribution version of 2.5.5).
+ */
+#if !defined(__GNUC__) || __GNUC__ < 2 || \
+ (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+#define __attribute__(x) /* delete __attribute__ if non-gcc or gcc1 */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define __dead __volatile
+#define __pure __const
+#endif
+#endif
+
+/* Delete pseudo-keywords wherever they are not available or needed. */
+#ifndef __dead
+#define __dead
+#define __pure
+#endif
+
+#endif /* !_CDEFS_H_ */
diff --git a/contrib/sendmail/src/clock.c b/contrib/sendmail/src/clock.c
new file mode 100644
index 0000000..e81c972
--- /dev/null
+++ b/contrib/sendmail/src/clock.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)clock.c 8.34 (Berkeley) 6/4/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+# ifndef sigmask
+# define sigmask(s) (1 << ((s) - 1))
+# endif
+
+/*
+** SETEVENT -- set an event to happen at a specific time.
+**
+** Events are stored in a sorted list for fast processing.
+** An event only applies to the process that set it.
+**
+** Parameters:
+** intvl -- intvl until next event occurs.
+** func -- function to call on event.
+** arg -- argument to func on event.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+*/
+
+EVENT *FreeEventList; /* list of free events */
+
+static SIGFUNC_DECL tick __P((int));
+
+EVENT *
+setevent(intvl, func, arg)
+ time_t intvl;
+ void (*func)();
+ int arg;
+{
+ register EVENT **evp;
+ register EVENT *ev;
+ auto time_t now;
+ int wasblocked;
+
+ if (intvl <= 0)
+ {
+ syserr("554 setevent: intvl=%ld\n", intvl);
+ return (NULL);
+ }
+
+ wasblocked = blocksignal(SIGALRM);
+ (void) time(&now);
+
+ /* search event queue for correct position */
+ for (evp = &EventQueue; (ev = *evp) != NULL; evp = &ev->ev_link)
+ {
+ if (ev->ev_time >= now + intvl)
+ break;
+ }
+
+ /* insert new event */
+ ev = FreeEventList;
+ if (ev == NULL)
+ ev = (EVENT *) xalloc(sizeof *ev);
+ else
+ FreeEventList = ev->ev_link;
+ ev->ev_time = now + intvl;
+ ev->ev_func = func;
+ ev->ev_arg = arg;
+ ev->ev_pid = getpid();
+ ev->ev_link = *evp;
+ *evp = ev;
+
+ if (tTd(5, 5))
+ printf("setevent: intvl=%ld, for=%ld, func=%lx, arg=%d, ev=%lx\n",
+ (long) intvl, (long)(now + intvl), (u_long) func,
+ arg, (u_long) ev);
+
+ setsignal(SIGALRM, tick);
+ intvl = EventQueue->ev_time - now;
+ (void) alarm((unsigned) intvl < 1 ? 1 : intvl);
+ if (wasblocked == 0)
+ (void) releasesignal(SIGALRM);
+ return (ev);
+}
+ /*
+** CLREVENT -- remove an event from the event queue.
+**
+** Parameters:
+** ev -- pointer to event to remove.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** arranges for event ev to not happen.
+*/
+
+void
+clrevent(ev)
+ register EVENT *ev;
+{
+ register EVENT **evp;
+ int wasblocked;
+
+ if (tTd(5, 5))
+ printf("clrevent: ev=%lx\n", (u_long) ev);
+ if (ev == NULL)
+ return;
+
+ /* find the parent event */
+ wasblocked = blocksignal(SIGALRM);
+ for (evp = &EventQueue; *evp != NULL; evp = &(*evp)->ev_link)
+ {
+ if (*evp == ev)
+ break;
+ }
+
+ /* now remove it */
+ if (*evp != NULL)
+ {
+ *evp = ev->ev_link;
+ ev->ev_link = FreeEventList;
+ FreeEventList = ev;
+ }
+
+ /* restore clocks and pick up anything spare */
+ if (wasblocked == 0)
+ releasesignal(SIGALRM);
+ if (EventQueue != NULL)
+ kill(getpid(), SIGALRM);
+}
+ /*
+** TICK -- take a clock tick
+**
+** Called by the alarm clock. This routine runs events as needed.
+** Always called as a signal handler, so we assume that SIGALRM
+** has been blocked.
+**
+** Parameters:
+** One that is ignored; for compatibility with signal handlers.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** calls the next function in EventQueue.
+*/
+
+/* ARGSUSED */
+static SIGFUNC_DECL
+tick(sig)
+ int sig;
+{
+ register time_t now;
+ register EVENT *ev;
+ int mypid = getpid();
+ int olderrno = errno;
+
+ (void) alarm(0);
+ now = curtime();
+
+ if (tTd(5, 4))
+ printf("tick: now=%ld\n", (long) now);
+
+ /* reset signal in case System V semantics */
+ (void) setsignal(SIGALRM, tick);
+ while ((ev = EventQueue) != NULL &&
+ (ev->ev_time <= now || ev->ev_pid != mypid))
+ {
+ void (*f)();
+ int arg;
+ int pid;
+
+ /* process the event on the top of the queue */
+ ev = EventQueue;
+ EventQueue = EventQueue->ev_link;
+ if (tTd(5, 6))
+ printf("tick: ev=%lx, func=%lx, arg=%d, pid=%d\n",
+ (u_long) ev, (u_long) ev->ev_func,
+ ev->ev_arg, ev->ev_pid);
+
+ /* we must be careful in here because ev_func may not return */
+ f = ev->ev_func;
+ arg = ev->ev_arg;
+ pid = ev->ev_pid;
+ ev->ev_link = FreeEventList;
+ FreeEventList = ev;
+ if (pid != getpid())
+ continue;
+ if (EventQueue != NULL)
+ {
+ if (EventQueue->ev_time > now)
+ (void) alarm((unsigned) (EventQueue->ev_time - now));
+ else
+ (void) alarm(3);
+ }
+
+ /* call ev_func */
+ errno = olderrno;
+ (*f)(arg);
+ (void) alarm(0);
+ now = curtime();
+ }
+ if (EventQueue != NULL)
+ (void) alarm((unsigned) (EventQueue->ev_time - now));
+ errno = olderrno;
+ return SIGFUNC_RETURN;
+}
+ /*
+** SLEEP -- a version of sleep that works with this stuff
+**
+** Because sleep uses the alarm facility, I must reimplement
+** it here.
+**
+** Parameters:
+** intvl -- time to sleep.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** waits for intvl time. However, other events can
+** be run during that interval.
+*/
+
+static bool SleepDone;
+static void endsleep __P((void));
+
+#ifndef SLEEP_T
+# define SLEEP_T unsigned int
+#endif
+
+SLEEP_T
+sleep(intvl)
+ unsigned int intvl;
+{
+ int was_held;
+
+ if (intvl == 0)
+ return (SLEEP_T) 0;
+ SleepDone = FALSE;
+ (void) setevent((time_t) intvl, endsleep, 0);
+ was_held = releasesignal(SIGALRM);
+ while (!SleepDone)
+ pause();
+ if (was_held > 0)
+ blocksignal(SIGALRM);
+ return (SLEEP_T) 0;
+}
+
+static void
+endsleep()
+{
+ SleepDone = TRUE;
+}
diff --git a/contrib/sendmail/src/collect.c b/contrib/sendmail/src/collect.c
new file mode 100644
index 0000000..190e699
--- /dev/null
+++ b/contrib/sendmail/src/collect.c
@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)collect.c 8.89 (Berkeley) 6/4/98";
+#endif /* not lint */
+
+# include <errno.h>
+# include "sendmail.h"
+
+/*
+** COLLECT -- read & parse message header & make temp file.
+**
+** Creates a temporary file name and copies the standard
+** input to that file. Leading UNIX-style "From" lines are
+** stripped off (after important information is extracted).
+**
+** Parameters:
+** fp -- file to read.
+** smtpmode -- if set, we are running SMTP: give an RFC821
+** style message to say we are ready to collect
+** input, and never ignore a single dot to mean
+** end of message.
+** hdrp -- the location to stash the header.
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Temp file is created and filled.
+** The from person may be set.
+*/
+
+static jmp_buf CtxCollectTimeout;
+static void collecttimeout __P((time_t));
+static bool CollectProgress;
+static EVENT *CollectTimeout;
+
+/* values for input state machine */
+#define IS_NORM 0 /* middle of line */
+#define IS_BOL 1 /* beginning of line */
+#define IS_DOT 2 /* read a dot at beginning of line */
+#define IS_DOTCR 3 /* read ".\r" at beginning of line */
+#define IS_CR 4 /* read a carriage return */
+
+/* values for message state machine */
+#define MS_UFROM 0 /* reading Unix from line */
+#define MS_HEADER 1 /* reading message header */
+#define MS_BODY 2 /* reading message body */
+
+void
+collect(fp, smtpmode, hdrp, e)
+ FILE *fp;
+ bool smtpmode;
+ HDR **hdrp;
+ register ENVELOPE *e;
+{
+ register FILE *volatile tf;
+ volatile bool ignrdot = smtpmode ? FALSE : IgnrDot;
+ volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
+ register char *volatile bp;
+ volatile int c = EOF;
+ volatile bool inputerr = FALSE;
+ bool headeronly;
+ char *volatile buf;
+ volatile int buflen;
+ volatile int istate;
+ volatile int mstate;
+ u_char *volatile pbp;
+ u_char peekbuf[8];
+ char dfname[MAXQFNAME];
+ char bufbuf[MAXLINE];
+ extern bool isheader __P((char *));
+ extern void eatheader __P((ENVELOPE *, bool));
+ extern void tferror __P((FILE *volatile, ENVELOPE *));
+
+ headeronly = hdrp != NULL;
+
+ /*
+ ** Create the temp file name and create the file.
+ */
+
+ if (!headeronly)
+ {
+ int tfd;
+ struct stat stbuf;
+
+ strcpy(dfname, queuename(e, 'd'));
+ tfd = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode, SFF_ANYFILE);
+ if (tfd < 0 || (tf = fdopen(tfd, "w")) == NULL)
+ {
+ syserr("Cannot create %s", dfname);
+ e->e_flags |= EF_NO_BODY_RETN;
+ finis();
+ }
+ if (fstat(fileno(tf), &stbuf) < 0)
+ e->e_dfino = -1;
+ else
+ {
+ e->e_dfdev = stbuf.st_dev;
+ e->e_dfino = stbuf.st_ino;
+ }
+ HasEightBits = FALSE;
+ e->e_msgsize = 0;
+ e->e_flags |= EF_HAS_DF;
+ }
+
+ /*
+ ** Tell ARPANET to go ahead.
+ */
+
+ if (smtpmode)
+ message("354 Enter mail, end with \".\" on a line by itself");
+
+ if (tTd(30, 2))
+ printf("collect\n");
+
+ /*
+ ** Read the message.
+ **
+ ** This is done using two interleaved state machines.
+ ** The input state machine is looking for things like
+ ** hidden dots; the message state machine is handling
+ ** the larger picture (e.g., header versus body).
+ */
+
+ buf = bp = bufbuf;
+ buflen = sizeof bufbuf;
+ pbp = peekbuf;
+ istate = IS_BOL;
+ mstate = SaveFrom ? MS_HEADER : MS_UFROM;
+ CollectProgress = FALSE;
+
+ if (dbto != 0)
+ {
+ /* handle possible input timeout */
+ if (setjmp(CtxCollectTimeout) != 0)
+ {
+ if (LogLevel > 2)
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "timeout waiting for input from %s during message collect",
+ CurHostName ? CurHostName : "<local machine>");
+ errno = 0;
+ usrerr("451 timeout waiting for input during message collect");
+ goto readerr;
+ }
+ CollectTimeout = setevent(dbto, collecttimeout, dbto);
+ }
+
+ for (;;)
+ {
+ extern int chompheader __P((char *, bool, HDR **, ENVELOPE *));
+
+ if (tTd(30, 35))
+ printf("top, istate=%d, mstate=%d\n", istate, mstate);
+ for (;;)
+ {
+ if (pbp > peekbuf)
+ c = *--pbp;
+ else
+ {
+ while (!feof(fp) && !ferror(fp))
+ {
+ errno = 0;
+ c = getc(fp);
+ if (errno != EINTR)
+ break;
+ clearerr(fp);
+ }
+ CollectProgress = TRUE;
+ if (TrafficLogFile != NULL && !headeronly)
+ {
+ if (istate == IS_BOL)
+ fprintf(TrafficLogFile, "%05d <<< ",
+ (int) getpid());
+ if (c == EOF)
+ fprintf(TrafficLogFile, "[EOF]\n");
+ else
+ putc(c, TrafficLogFile);
+ }
+ if (c == EOF)
+ goto readerr;
+ if (SevenBitInput)
+ c &= 0x7f;
+ else
+ HasEightBits |= bitset(0x80, c);
+ }
+ if (tTd(30, 94))
+ printf("istate=%d, c=%c (0x%x)\n",
+ istate, c, c);
+ switch (istate)
+ {
+ case IS_BOL:
+ if (c == '.')
+ {
+ istate = IS_DOT;
+ continue;
+ }
+ break;
+
+ case IS_DOT:
+ if (c == '\n' && !ignrdot &&
+ !bitset(EF_NL_NOT_EOL, e->e_flags))
+ goto readerr;
+ else if (c == '\r' &&
+ !bitset(EF_CRLF_NOT_EOL, e->e_flags))
+ {
+ istate = IS_DOTCR;
+ continue;
+ }
+ else if (c != '.' ||
+ (OpMode != MD_SMTP &&
+ OpMode != MD_DAEMON &&
+ OpMode != MD_ARPAFTP))
+ {
+ *pbp++ = c;
+ c = '.';
+ }
+ break;
+
+ case IS_DOTCR:
+ if (c == '\n' && !ignrdot)
+ goto readerr;
+ else
+ {
+ /* push back the ".\rx" */
+ *pbp++ = c;
+ *pbp++ = '\r';
+ c = '.';
+ }
+ break;
+
+ case IS_CR:
+ if (c == '\n')
+ istate = IS_BOL;
+ else
+ {
+ ungetc(c, fp);
+ c = '\r';
+ istate = IS_NORM;
+ }
+ goto bufferchar;
+ }
+
+ if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags))
+ {
+ istate = IS_CR;
+ continue;
+ }
+ else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags))
+ istate = IS_BOL;
+ else
+ istate = IS_NORM;
+
+bufferchar:
+ if (!headeronly)
+ e->e_msgsize++;
+ if (mstate == MS_BODY)
+ {
+ /* just put the character out */
+ if (MaxMessageSize <= 0 ||
+ e->e_msgsize <= MaxMessageSize)
+ putc(c, tf);
+ continue;
+ }
+
+ /* header -- buffer up */
+ if (bp >= &buf[buflen - 2])
+ {
+ char *obuf;
+
+ if (mstate != MS_HEADER)
+ break;
+
+ /* out of space for header */
+ obuf = buf;
+ if (buflen < MEMCHUNKSIZE)
+ buflen *= 2;
+ else
+ buflen += MEMCHUNKSIZE;
+ buf = xalloc(buflen);
+ bcopy(obuf, buf, bp - obuf);
+ bp = &buf[bp - obuf];
+ if (obuf != bufbuf)
+ free(obuf);
+ }
+ if (c >= 0200 && c <= 0237)
+ {
+#if 0 /* causes complaints -- figure out something for 8.9 */
+ usrerr("Illegal character 0x%x in header", c);
+#endif
+ }
+ else if (c != '\0')
+ *bp++ = c;
+ if (istate == IS_BOL)
+ break;
+ }
+ *bp = '\0';
+
+nextstate:
+ if (tTd(30, 35))
+ printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
+ istate, mstate, buf);
+ switch (mstate)
+ {
+ case MS_UFROM:
+ mstate = MS_HEADER;
+#ifndef NOTUNIX
+ if (strncmp(buf, "From ", 5) == 0)
+ {
+ extern void eatfrom __P((char *volatile, ENVELOPE *));
+
+ bp = buf;
+ eatfrom(buf, e);
+ continue;
+ }
+#endif
+ /* fall through */
+
+ case MS_HEADER:
+ if (!isheader(buf))
+ {
+ mstate = MS_BODY;
+ goto nextstate;
+ }
+
+ /* check for possible continuation line */
+ do
+ {
+ clearerr(fp);
+ errno = 0;
+ c = getc(fp);
+ } while (errno == EINTR);
+ if (c != EOF)
+ ungetc(c, fp);
+ if (c == ' ' || c == '\t')
+ {
+ /* yep -- defer this */
+ continue;
+ }
+
+ /* trim off trailing CRLF or NL */
+ if (*--bp != '\n' || *--bp != '\r')
+ bp++;
+ *bp = '\0';
+ if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e)))
+ {
+ mstate = MS_BODY;
+ goto nextstate;
+ }
+ break;
+
+ case MS_BODY:
+ if (tTd(30, 1))
+ printf("EOH\n");
+ if (headeronly)
+ goto readerr;
+ bp = buf;
+
+ /* toss blank line */
+ if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) &&
+ bp[0] == '\r' && bp[1] == '\n') ||
+ (!bitset(EF_NL_NOT_EOL, e->e_flags) &&
+ bp[0] == '\n'))
+ {
+ break;
+ }
+
+ /* if not a blank separator, write it out */
+ if (MaxMessageSize <= 0 ||
+ e->e_msgsize <= MaxMessageSize)
+ {
+ while (*bp != '\0')
+ putc(*bp++, tf);
+ }
+ break;
+ }
+ bp = buf;
+ }
+
+readerr:
+ if ((feof(fp) && smtpmode) || ferror(fp))
+ {
+ const char *errmsg = errstring(errno);
+
+ if (tTd(30, 1))
+ printf("collect: premature EOM: %s\n", errmsg);
+ if (LogLevel >= 2)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "collect: premature EOM: %s", errmsg);
+ inputerr = TRUE;
+ }
+
+ /* reset global timer */
+ clrevent(CollectTimeout);
+
+ if (headeronly)
+ return;
+
+ if (tf != NULL &&
+ (fflush(tf) != 0 || ferror(tf) ||
+ (SuperSafe && fsync(fileno(tf)) < 0) ||
+ fclose(tf) < 0))
+ {
+ tferror(tf, e);
+ flush_errors(TRUE);
+ finis();
+ }
+
+ /* An EOF when running SMTP is an error */
+ if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
+ {
+ char *host;
+ char *problem;
+
+ host = RealHostName;
+ if (host == NULL)
+ host = "localhost";
+
+ if (feof(fp))
+ problem = "unexpected close";
+ else if (ferror(fp))
+ problem = "I/O error";
+ else
+ problem = "read timeout";
+ if (LogLevel > 0 && feof(fp))
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "collect: %s on connection from %.100s, sender=%s: %s",
+ problem, host,
+ shortenstring(e->e_from.q_paddr, MAXSHORTSTR),
+ errstring(errno));
+ if (feof(fp))
+ usrerr("451 collect: %s on connection from %s, from=%s",
+ problem, host,
+ shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
+ else
+ syserr("451 collect: %s on connection from %s, from=%s",
+ problem, host,
+ shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
+
+ /* don't return an error indication */
+ e->e_to = NULL;
+ e->e_flags &= ~EF_FATALERRS;
+ e->e_flags |= EF_CLRQUEUE;
+
+ /* and don't try to deliver the partial message either */
+ if (InChild)
+ ExitStat = EX_QUIT;
+ finis();
+ }
+
+ /*
+ ** Find out some information from the headers.
+ ** Examples are who is the from person & the date.
+ */
+
+ eatheader(e, TRUE);
+
+ if (GrabTo && e->e_sendqueue == NULL)
+ usrerr("No recipient addresses found in header");
+
+ /* collect statistics */
+ if (OpMode != MD_VERIFY)
+ markstats(e, (ADDRESS *) NULL, FALSE);
+
+#if _FFR_DSN_RRT_OPTION
+ /*
+ ** If we have a Return-Receipt-To:, turn it into a DSN.
+ */
+
+ if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL)
+ {
+ ADDRESS *q;
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ if (!bitset(QHASNOTIFY, q->q_flags))
+ q->q_flags |= QHASNOTIFY|QPINGONSUCCESS;
+ }
+#endif
+
+ /*
+ ** Add an Apparently-To: line if we have no recipient lines.
+ */
+
+ if (hvalue("to", e->e_header) != NULL ||
+ hvalue("cc", e->e_header) != NULL ||
+ hvalue("apparently-to", e->e_header) != NULL)
+ {
+ /* have a valid recipient header -- delete Bcc: headers */
+ e->e_flags |= EF_DELETE_BCC;
+ }
+ else if (hvalue("bcc", e->e_header) == NULL)
+ {
+ /* no valid recipient headers */
+ register ADDRESS *q;
+ char *hdr = NULL;
+ extern void addheader __P((char *, char *, HDR **));
+
+ /* create an Apparently-To: field */
+ /* that or reject the message.... */
+ switch (NoRecipientAction)
+ {
+ case NRA_ADD_APPARENTLY_TO:
+ hdr = "Apparently-To";
+ break;
+
+ case NRA_ADD_TO:
+ hdr = "To";
+ break;
+
+ case NRA_ADD_BCC:
+ addheader("Bcc", " ", &e->e_header);
+ break;
+
+ case NRA_ADD_TO_UNDISCLOSED:
+ addheader("To", "undisclosed-recipients:;", &e->e_header);
+ break;
+ }
+
+ if (hdr != NULL)
+ {
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (q->q_alias != NULL)
+ continue;
+ if (tTd(30, 3))
+ printf("Adding %s: %s\n",
+ hdr, q->q_paddr);
+ addheader(hdr, q->q_paddr, &e->e_header);
+ }
+ }
+ }
+
+ /* check for message too large */
+ if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
+ {
+ e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
+ e->e_status = "5.2.3";
+ usrerr("552 Message exceeds maximum fixed size (%ld)",
+ MaxMessageSize);
+ if (LogLevel > 6)
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "message size (%ld) exceeds maximum (%ld)",
+ e->e_msgsize, MaxMessageSize);
+ }
+
+ /* check for illegal 8-bit data */
+ if (HasEightBits)
+ {
+ e->e_flags |= EF_HAS8BIT;
+ if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) &&
+ !bitset(EF_IS_MIME, e->e_flags))
+ {
+ e->e_status = "5.6.1";
+ usrerr("554 Eight bit data not allowed");
+ }
+ }
+ else
+ {
+ /* if it claimed to be 8 bits, well, it lied.... */
+ if (e->e_bodytype != NULL &&
+ strcasecmp(e->e_bodytype, "8BITMIME") == 0)
+ e->e_bodytype = "7BIT";
+ }
+
+ if ((e->e_dfp = fopen(dfname, "r")) == NULL)
+ {
+ /* we haven't acked receipt yet, so just chuck this */
+ syserr("Cannot reopen %s", dfname);
+ finis();
+ }
+}
+
+
+static void
+collecttimeout(timeout)
+ time_t timeout;
+{
+ /* if no progress was made, die now */
+ if (!CollectProgress)
+ longjmp(CtxCollectTimeout, 1);
+
+ /* otherwise reset the timeout */
+ CollectTimeout = setevent(timeout, collecttimeout, timeout);
+ CollectProgress = FALSE;
+}
+ /*
+** TFERROR -- signal error on writing the temporary file.
+**
+** Parameters:
+** tf -- the file pointer for the temporary file.
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Gives an error message.
+** Arranges for following output to go elsewhere.
+*/
+
+void
+tferror(tf, e)
+ FILE *volatile tf;
+ register ENVELOPE *e;
+{
+ setstat(EX_IOERR);
+ if (errno == ENOSPC)
+ {
+#if STAT64 > 0
+ struct stat64 st;
+#else
+ struct stat st;
+#endif
+ long avail;
+ long bsize;
+ extern long freediskspace __P((char *, long *));
+
+ e->e_flags |= EF_NO_BODY_RETN;
+
+ if (
+#if STAT64 > 0
+ fstat64(fileno(tf), &st)
+#else
+ fstat(fileno(tf), &st)
+#endif
+ < 0)
+ st.st_size = 0;
+ (void) freopen(queuename(e, 'd'), "w", tf);
+ if (st.st_size <= 0)
+ fprintf(tf, "\n*** Mail could not be accepted");
+ else if (sizeof st.st_size > sizeof (long))
+ fprintf(tf, "\n*** Mail of at least %s bytes could not be accepted\n",
+ quad_to_string(st.st_size));
+ else
+ fprintf(tf, "\n*** Mail of at least %lu bytes could not be accepted\n",
+ (unsigned long) st.st_size);
+ fprintf(tf, "*** at %s due to lack of disk space for temp file.\n",
+ MyHostName);
+ avail = freediskspace(QueueDir, &bsize);
+ if (avail > 0)
+ {
+ if (bsize > 1024)
+ avail *= bsize / 1024;
+ else if (bsize < 1024)
+ avail /= 1024 / bsize;
+ fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n",
+ avail);
+ }
+ e->e_status = "4.3.1";
+ usrerr("452 Out of disk space for temp file");
+ }
+ else
+ syserr("collect: Cannot write tf%s", e->e_id);
+ if (freopen("/dev/null", "w", tf) == NULL)
+ sm_syslog(LOG_ERR, e->e_id,
+ "tferror: freopen(\"/dev/null\") failed: %s",
+ errstring(errno));
+}
+ /*
+** EATFROM -- chew up a UNIX style from line and process
+**
+** This does indeed make some assumptions about the format
+** of UNIX messages.
+**
+** Parameters:
+** fm -- the from line.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** extracts what information it can from the header,
+** such as the date.
+*/
+
+# ifndef NOTUNIX
+
+char *DowList[] =
+{
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
+};
+
+char *MonthList[] =
+{
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ NULL
+};
+
+void
+eatfrom(fm, e)
+ char *volatile fm;
+ register ENVELOPE *e;
+{
+ register char *p;
+ register char **dt;
+
+ if (tTd(30, 2))
+ printf("eatfrom(%s)\n", fm);
+
+ /* find the date part */
+ p = fm;
+ while (*p != '\0')
+ {
+ /* skip a word */
+ while (*p != '\0' && *p != ' ')
+ p++;
+ while (*p == ' ')
+ p++;
+ if (!(isascii(*p) && isupper(*p)) ||
+ p[3] != ' ' || p[13] != ':' || p[16] != ':')
+ continue;
+
+ /* we have a possible date */
+ for (dt = DowList; *dt != NULL; dt++)
+ if (strncmp(*dt, p, 3) == 0)
+ break;
+ if (*dt == NULL)
+ continue;
+
+ for (dt = MonthList; *dt != NULL; dt++)
+ if (strncmp(*dt, &p[4], 3) == 0)
+ break;
+ if (*dt != NULL)
+ break;
+ }
+
+ if (*p != '\0')
+ {
+ char *q;
+
+ /* we have found a date */
+ q = xalloc(25);
+ (void) strncpy(q, p, 25);
+ q[24] = '\0';
+ q = arpadate(q);
+ define('a', newstr(q), e);
+ }
+}
+
+# endif /* NOTUNIX */
diff --git a/contrib/sendmail/src/conf.c b/contrib/sendmail/src/conf.c
new file mode 100644
index 0000000..838cd17
--- /dev/null
+++ b/contrib/sendmail/src/conf.c
@@ -0,0 +1,4798 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)conf.c 8.431 (Berkeley) 6/25/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include "pathnames.h"
+# include <sys/ioctl.h>
+# include <sys/param.h>
+# include <limits.h>
+
+/*
+** CONF.C -- Sendmail Configuration Tables.
+**
+** Defines the configuration of this installation.
+**
+** Configuration Variables:
+** HdrInfo -- a table describing well-known header fields.
+** Each entry has the field name and some flags,
+** which are described in sendmail.h.
+**
+** Notes:
+** I have tried to put almost all the reasonable
+** configuration information into the configuration
+** file read at runtime. My intent is that anything
+** here is a function of the version of UNIX you
+** are running, or is really static -- for example
+** the headers are a superset of widely used
+** protocols. If you find yourself playing with
+** this file too much, you may be making a mistake!
+*/
+
+
+/*
+** Header info table
+** Final (null) entry contains the flags used for any other field.
+**
+** Not all of these are actually handled specially by sendmail
+** at this time. They are included as placeholders, to let
+** you know that "someday" I intend to have sendmail do
+** something with them.
+*/
+
+struct hdrinfo HdrInfo[] =
+{
+ /* originator fields, most to least significant */
+ { "resent-sender", H_FROM|H_RESENT },
+ { "resent-from", H_FROM|H_RESENT },
+ { "resent-reply-to", H_FROM|H_RESENT },
+ { "sender", H_FROM },
+ { "from", H_FROM },
+ { "reply-to", H_FROM },
+ { "errors-to", H_FROM|H_ERRORSTO },
+ { "full-name", H_ACHECK },
+ { "return-receipt-to", H_RECEIPTTO },
+
+ /* destination fields */
+ { "to", H_RCPT },
+ { "resent-to", H_RCPT|H_RESENT },
+ { "cc", H_RCPT },
+ { "resent-cc", H_RCPT|H_RESENT },
+ { "bcc", H_RCPT|H_BCC },
+ { "resent-bcc", H_RCPT|H_BCC|H_RESENT },
+ { "apparently-to", H_RCPT },
+
+ /* message identification and control */
+ { "message-id", 0 },
+ { "resent-message-id", H_RESENT },
+ { "message", H_EOH },
+ { "text", H_EOH },
+
+ /* date fields */
+ { "date", 0 },
+ { "resent-date", H_RESENT },
+
+ /* trace fields */
+ { "received", H_TRACE|H_FORCE },
+ { "x400-received", H_TRACE|H_FORCE },
+ { "via", H_TRACE|H_FORCE },
+ { "mail-from", H_TRACE|H_FORCE },
+
+ /* miscellaneous fields */
+ { "comments", H_FORCE|H_ENCODABLE },
+ { "return-path", H_FORCE|H_ACHECK },
+ { "content-transfer-encoding", H_CTE },
+ { "content-type", H_CTYPE },
+ { "content-length", H_ACHECK },
+ { "subject", H_ENCODABLE },
+
+ { NULL, 0 }
+};
+
+
+
+/*
+** Privacy values
+*/
+
+struct prival PrivacyValues[] =
+{
+ { "public", PRIV_PUBLIC },
+ { "needmailhelo", PRIV_NEEDMAILHELO },
+ { "needexpnhelo", PRIV_NEEDEXPNHELO },
+ { "needvrfyhelo", PRIV_NEEDVRFYHELO },
+ { "noexpn", PRIV_NOEXPN },
+ { "novrfy", PRIV_NOVRFY },
+ { "restrictmailq", PRIV_RESTRICTMAILQ },
+ { "restrictqrun", PRIV_RESTRICTQRUN },
+ { "noetrn", PRIV_NOETRN },
+ { "noverb", PRIV_NOVERB },
+ { "authwarnings", PRIV_AUTHWARNINGS },
+ { "noreceipts", PRIV_NORECEIPTS },
+ { "goaway", PRIV_GOAWAY },
+ { NULL, 0 }
+};
+
+/*
+** DontBlameSendmail values
+*/
+struct dbsval DontBlameSendmailValues[] =
+{
+ { "safe", DBS_SAFE },
+ { "assumesafechown", DBS_ASSUMESAFECHOWN },
+ { "groupwritabledirpathsafe", DBS_GROUPWRITABLEDIRPATHSAFE },
+ { "groupwritableforwardfilesafe",
+ DBS_GROUPWRITABLEFORWARDFILESAFE },
+ { "groupwritableincludefilesafe",
+ DBS_GROUPWRITABLEINCLUDEFILESAFE },
+ { "groupwritablealiasfile", DBS_GROUPWRITABLEALIASFILE },
+ { "worldwritablealiasfile", DBS_WORLDWRITABLEALIASFILE },
+ { "forwardfileinunsafedirpath", DBS_FORWARDFILEINUNSAFEDIRPATH },
+ { "includefileinunsafedirpath", DBS_INCLUDEFILEINUNSAFEDIRPATH },
+ { "mapinunsafedirpath", DBS_MAPINUNSAFEDIRPATH },
+ { "linkedaliasfileinwritabledir",
+ DBS_LINKEDALIASFILEINWRITABLEDIR },
+ { "linkedclassfileinwritabledir",
+ DBS_LINKEDCLASSFILEINWRITABLEDIR },
+ { "linkedforwardfileinwritabledir",
+ DBS_LINKEDFORWARDFILEINWRITABLEDIR },
+ { "linkedincludefileinwritabledir",
+ DBS_LINKEDINCLUDEFILEINWRITABLEDIR },
+ { "linkedmapinwritabledir", DBS_LINKEDMAPINWRITABLEDIR },
+ { "linkedserviceswitchfileinwritabledir",
+ DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR },
+ { "filedeliverytohardlink", DBS_FILEDELIVERYTOHARDLINK },
+ { "filedeliverytosymlink", DBS_FILEDELIVERYTOSYMLINK },
+ { "writemaptohardlink", DBS_WRITEMAPTOHARDLINK },
+ { "writemaptosymlink", DBS_WRITEMAPTOSYMLINK },
+ { "writestatstohardlink", DBS_WRITESTATSTOHARDLINK },
+ { "writestatstosymlink", DBS_WRITESTATSTOSYMLINK },
+ { "forwardfileingroupwritabledirpath",
+ DBS_FORWARDFILEINGROUPWRITABLEDIRPATH },
+ { "includefileingroupwritabledirpath",
+ DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH },
+ { "classfileinunsafedirpath", DBS_CLASSFILEINUNSAFEDIRPATH },
+ { "errorheaderinunsafedirpath", DBS_ERRORHEADERINUNSAFEDIRPATH },
+ { "helpfileinunsafedirpath", DBS_HELPFILEINUNSAFEDIRPATH },
+ { "forwardfileinunsafedirpathsafe",
+ DBS_FORWARDFILEINUNSAFEDIRPATHSAFE },
+ { "includefileinunsafedirpathsafe",
+ DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE },
+ { "runprograminunsafedirpath", DBS_RUNPROGRAMINUNSAFEDIRPATH },
+ { "runwritableprogram", DBS_RUNWRITABLEPROGRAM },
+ { NULL, 0 }
+};
+
+
+/*
+** Miscellaneous stuff.
+*/
+
+int DtableSize = 50; /* max open files; reset in 4.2bsd */
+ /*
+** SETDEFAULTS -- set default values
+**
+** Because of the way freezing is done, these must be initialized
+** using direct code.
+**
+** Parameters:
+** e -- the default envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Initializes a bunch of global variables to their
+** default values.
+*/
+
+#define MINUTES * 60
+#define HOURS * 60 MINUTES
+#define DAYS * 24 HOURS
+
+#ifndef _PATH_VARTMP
+# define _PATH_VARTMP "/usr/tmp/"
+#endif
+
+#ifndef MAXRULERECURSION
+# define MAXRULERECURSION 50 /* max ruleset recursion depth */
+#endif
+
+void
+setdefaults(e)
+ register ENVELOPE *e;
+{
+ int i;
+ struct passwd *pw;
+ char buf[MAXNAME];
+ extern void inittimeouts __P((char *));
+ extern void setdefuser __P((void));
+ extern void setupmaps __P((void));
+ extern void setupmailers __P((void));
+ extern void setupheaders __P((void));
+
+ SpaceSub = ' '; /* option B */
+ QueueLA = 8; /* option x */
+ RefuseLA = 12; /* option X */
+ WkRecipFact = 30000L; /* option y */
+ WkClassFact = 1800L; /* option z */
+ WkTimeFact = 90000L; /* option Z */
+ QueueFactor = WkRecipFact * 20; /* option q */
+ FileMode = (RealUid != geteuid()) ? 0644 : 0600;
+ /* option F */
+
+ if (((pw = getpwnam("mailnull")) != NULL && pw->pw_uid != 0) ||
+ ((pw = getpwnam("sendmail")) != NULL && pw->pw_uid != 0) ||
+ ((pw = getpwnam("daemon")) != NULL && pw->pw_uid != 0))
+ {
+ DefUid = pw->pw_uid; /* option u */
+ DefGid = pw->pw_gid; /* option g */
+ DefUser = newstr(pw->pw_name);
+ }
+ else
+ {
+ DefUid = 1; /* option u */
+ DefGid = 1; /* option g */
+ setdefuser();
+ }
+ TrustedFileUid = 0;
+ if (tTd(37, 4))
+ printf("setdefaults: DefUser=%s, DefUid=%d, DefGid=%d\n",
+ DefUser != NULL ? DefUser : "<1:1>",
+ (int) DefUid, (int) DefGid);
+ CheckpointInterval = 10; /* option C */
+ MaxHopCount = 25; /* option h */
+ e->e_sendmode = SM_FORK; /* option d */
+ e->e_errormode = EM_PRINT; /* option e */
+ SevenBitInput = FALSE; /* option 7 */
+ MaxMciCache = 1; /* option k */
+ MciCacheTimeout = 5 MINUTES; /* option K */
+ LogLevel = 9; /* option L */
+ inittimeouts(NULL); /* option r */
+ PrivacyFlags = PRIV_PUBLIC; /* option p */
+ DontBlameSendmail = DBS_SAFE; /* DontBlameSendmail option */
+#if MIME8TO7
+ MimeMode = MM_CVTMIME|MM_PASS8BIT; /* option 8 */
+#else
+ MimeMode = MM_PASS8BIT;
+#endif
+ for (i = 0; i < MAXTOCLASS; i++)
+ {
+ TimeOuts.to_q_return[i] = 5 DAYS; /* option T */
+ TimeOuts.to_q_warning[i] = 0; /* option T */
+ }
+ ServiceSwitchFile = "/etc/service.switch";
+ ServiceCacheMaxAge = (time_t) 10;
+ HostsFile = _PATH_HOSTS;
+ PidFile = newstr(_PATH_SENDMAILPID);
+ MustQuoteChars = "@,;:\\()[].'";
+ MciInfoTimeout = 30 MINUTES;
+ MaxRuleRecursion = MAXRULERECURSION;
+ MaxAliasRecursion = 10;
+ MaxMacroRecursion = 10;
+ ColonOkInAddr = TRUE;
+ DontLockReadFiles = TRUE;
+ DoubleBounceAddr = "postmaster";
+ snprintf(buf, sizeof buf, "%s%sdead.letter",
+ _PATH_VARTMP,
+ _PATH_VARTMP[sizeof _PATH_VARTMP - 2] == '/' ? "" : "/");
+ DeadLetterDrop = newstr(buf);
+#ifdef HESIOD_INIT
+ HesiodContext = NULL;
+#endif
+ setupmaps();
+ setupmailers();
+ setupheaders();
+}
+
+
+/*
+** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups())
+*/
+
+void
+setdefuser()
+{
+ struct passwd *defpwent;
+ static char defuserbuf[40];
+
+ DefUser = defuserbuf;
+ defpwent = sm_getpwuid(DefUid);
+ snprintf(defuserbuf, sizeof defuserbuf, "%s",
+ defpwent == NULL ? "nobody" : defpwent->pw_name);
+ if (tTd(37, 4))
+ printf("setdefuser: DefUid=%d, DefUser=%s\n",
+ (int) DefUid, DefUser);
+}
+ /*
+** SETUPMAILERS -- initialize default mailers
+*/
+
+void
+setupmailers()
+{
+ char buf[100];
+ extern void makemailer __P((char *));
+
+ strcpy(buf, "prog, P=/bin/sh, F=lsoDq9, T=DNS/RFC822/X-Unix, A=sh -c \201u");
+ makemailer(buf);
+
+ strcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=DNS/RFC822/X-Unix, A=FILE \201u");
+ makemailer(buf);
+
+ strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u");
+ makemailer(buf);
+}
+ /*
+** SETUPMAPS -- set up map classes
+*/
+
+#define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \
+ { \
+ extern bool parse __P((MAP *, char *)); \
+ extern bool open __P((MAP *, int)); \
+ extern void close __P((MAP *)); \
+ extern char *lookup __P((MAP *, char *, char **, int *)); \
+ extern void store __P((MAP *, char *, char *)); \
+ s = stab(name, ST_MAPCLASS, ST_ENTER); \
+ s->s_mapclass.map_cname = name; \
+ s->s_mapclass.map_ext = ext; \
+ s->s_mapclass.map_cflags = flags; \
+ s->s_mapclass.map_parse = parse; \
+ s->s_mapclass.map_open = open; \
+ s->s_mapclass.map_close = close; \
+ s->s_mapclass.map_lookup = lookup; \
+ s->s_mapclass.map_store = store; \
+ }
+
+void
+setupmaps()
+{
+ register STAB *s;
+
+#ifdef NEWDB
+ MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
+ map_parseargs, hash_map_open, db_map_close,
+ db_map_lookup, db_map_store);
+
+ MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
+ map_parseargs, bt_map_open, db_map_close,
+ db_map_lookup, db_map_store);
+#endif
+
+#ifdef NDBM
+ MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE,
+ map_parseargs, ndbm_map_open, ndbm_map_close,
+ ndbm_map_lookup, ndbm_map_store);
+#endif
+
+#ifdef NIS
+ MAPDEF("nis", NULL, MCF_ALIASOK,
+ map_parseargs, nis_map_open, null_map_close,
+ nis_map_lookup, null_map_store);
+#endif
+
+#ifdef NISPLUS
+ MAPDEF("nisplus", NULL, MCF_ALIASOK,
+ map_parseargs, nisplus_map_open, null_map_close,
+ nisplus_map_lookup, null_map_store);
+#endif
+#ifdef LDAPMAP
+ MAPDEF("ldapx", NULL, 0,
+ ldap_map_parseargs, ldap_map_open, ldap_map_close,
+ ldap_map_lookup, null_map_store);
+#endif
+
+#ifdef HESIOD
+ MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY,
+ map_parseargs, hes_map_open, null_map_close,
+ hes_map_lookup, null_map_store);
+#endif
+
+#if NETINFO
+ MAPDEF("netinfo", NULL, MCF_ALIASOK,
+ map_parseargs, ni_map_open, null_map_close,
+ ni_map_lookup, null_map_store);
+#endif
+
+#if 0
+ MAPDEF("dns", NULL, 0,
+ dns_map_init, null_map_open, null_map_close,
+ dns_map_lookup, null_map_store);
+#endif
+
+#if NAMED_BIND
+ /* best MX DNS lookup */
+ MAPDEF("bestmx", NULL, MCF_OPTFILE,
+ map_parseargs, null_map_open, null_map_close,
+ bestmx_map_lookup, null_map_store);
+#endif
+
+ MAPDEF("host", NULL, 0,
+ host_map_init, null_map_open, null_map_close,
+ host_map_lookup, null_map_store);
+
+ MAPDEF("text", NULL, MCF_ALIASOK,
+ map_parseargs, text_map_open, null_map_close,
+ text_map_lookup, null_map_store);
+
+ MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY,
+ map_parseargs, stab_map_open, null_map_close,
+ stab_map_lookup, stab_map_store);
+
+ MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE,
+ map_parseargs, impl_map_open, impl_map_close,
+ impl_map_lookup, impl_map_store);
+
+ /* access to system passwd file */
+ MAPDEF("user", NULL, MCF_OPTFILE,
+ map_parseargs, user_map_open, null_map_close,
+ user_map_lookup, null_map_store);
+
+ /* dequote map */
+ MAPDEF("dequote", NULL, 0,
+ dequote_init, null_map_open, null_map_close,
+ dequote_map, null_map_store);
+
+#ifdef MAP_REGEX
+ MAPDEF("regex", NULL, 0,
+ regex_map_init, null_map_open, null_map_close,
+ regex_map_lookup, null_map_store);
+#endif
+
+#if USERDB
+ /* user database */
+ MAPDEF("userdb", ".db", 0,
+ map_parseargs, null_map_open, null_map_close,
+ udb_map_lookup, null_map_store);
+#endif
+
+ /* arbitrary programs */
+ MAPDEF("program", NULL, MCF_ALIASOK,
+ map_parseargs, null_map_open, null_map_close,
+ prog_map_lookup, null_map_store);
+
+ /* sequenced maps */
+ MAPDEF("sequence", NULL, MCF_ALIASOK,
+ seq_map_parse, null_map_open, null_map_close,
+ seq_map_lookup, seq_map_store);
+
+ /* switched interface to sequenced maps */
+ MAPDEF("switch", NULL, MCF_ALIASOK,
+ map_parseargs, switch_map_open, null_map_close,
+ seq_map_lookup, seq_map_store);
+
+ /* null map lookup -- really for internal use only */
+ MAPDEF("null", NULL, MCF_ALIASOK|MCF_OPTFILE,
+ map_parseargs, null_map_open, null_map_close,
+ null_map_lookup, null_map_store);
+
+#if _FFR_MAP_SYSLOG
+ /* syslog map -- logs information to syslog */
+ MAPDEF("syslog", NULL, 0,
+ syslog_map_parseargs, null_map_open, null_map_close,
+ syslog_map_lookup, null_map_store);
+#endif
+}
+
+#undef MAPDEF
+ /*
+** INITHOSTMAPS -- initial host-dependent maps
+**
+** This should act as an interface to any local service switch
+** provided by the host operating system.
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Should define maps "host" and "users" as necessary
+** for this OS. If they are not defined, they will get
+** a default value later. It should check to make sure
+** they are not defined first, since it's possible that
+** the config file has provided an override.
+*/
+
+void
+inithostmaps()
+{
+ register int i;
+ int nmaps;
+ char *maptype[MAXMAPSTACK];
+ short mapreturn[MAXMAPACTIONS];
+ char buf[MAXLINE];
+
+ /*
+ ** Set up default hosts maps.
+ */
+
+#if 0
+ nmaps = switch_map_find("hosts", maptype, mapreturn);
+ for (i = 0; i < nmaps; i++)
+ {
+ if (strcmp(maptype[i], "files") == 0 &&
+ stab("hosts.files", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts");
+ (void) makemapentry(buf);
+ }
+#if NAMED_BIND
+ else if (strcmp(maptype[i], "dns") == 0 &&
+ stab("hosts.dns", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.dns dns A");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NISPLUS
+ else if (strcmp(maptype[i], "nisplus") == 0 &&
+ stab("hosts.nisplus", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.nisplus nisplus -k name -v address -d hosts.org_dir");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NIS
+ else if (strcmp(maptype[i], "nis") == 0 &&
+ stab("hosts.nis", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.nis nis -d -k 0 -v 1 hosts.byname");
+ (void) makemapentry(buf);
+ }
+#endif
+#if NETINFO
+ else if (strcmp(maptype[i], "netinfo") == 0) &&
+ stab("hosts.netinfo", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.netinfo netinfo -v name /machines");
+ (void) makemapentry(buf);
+ }
+#endif
+ }
+#endif
+
+ /*
+ ** Make sure we have a host map.
+ */
+
+ if (stab("host", ST_MAP, ST_FIND) == NULL)
+ {
+ /* user didn't initialize: set up host map */
+ strcpy(buf, "host host");
+#if NAMED_BIND
+ if (ConfigLevel >= 2)
+ strcat(buf, " -a.");
+#endif
+ (void) makemapentry(buf);
+ }
+
+ /*
+ ** Set up default aliases maps
+ */
+
+ nmaps = switch_map_find("aliases", maptype, mapreturn);
+ for (i = 0; i < nmaps; i++)
+ {
+ if (strcmp(maptype[i], "files") == 0 &&
+ stab("aliases.files", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.files null");
+ (void) makemapentry(buf);
+ }
+#ifdef NISPLUS
+ else if (strcmp(maptype[i], "nisplus") == 0 &&
+ stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion -d mail_aliases.org_dir");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NIS
+ else if (strcmp(maptype[i], "nis") == 0 &&
+ stab("aliases.nis", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.nis nis -d mail.aliases");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NETINFO
+ else if (strcmp(maptype[i], "netinfo") == 0 &&
+ stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.netinfo netinfo -z, /aliases");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef HESIOD
+ else if (strcmp(maptype[i], "hesiod") == 0 &&
+ stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.hesiod hesiod aliases");
+ (void) makemapentry(buf);
+ }
+#endif
+ }
+ if (stab("aliases", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases switch aliases");
+ (void) makemapentry(buf);
+ }
+
+#if 0 /* "user" map class is a better choice */
+ /*
+ ** Set up default users maps.
+ */
+
+ nmaps = switch_map_find("passwd", maptype, mapreturn);
+ for (i = 0; i < nmaps; i++)
+ {
+ if (strcmp(maptype[i], "files") == 0 &&
+ stab("users.files", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd");
+ (void) makemapentry(buf);
+ }
+#ifdef NISPLUS
+ else if (strcmp(maptype[i], "nisplus") == 0 &&
+ stab("users.nisplus", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users.nisplus nisplus -m -kname -vhome -d passwd.org_dir");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NIS
+ else if (strcmp(maptype[i], "nis") == 0 &&
+ stab("users.nis", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users.nis nis -m -d passwd.byname");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef HESIOD
+ else if (strcmp(maptype[i], "hesiod") == 0) &&
+ stab("users.hesiod", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users.hesiod hesiod");
+ (void) makemapentry(buf);
+ }
+#endif
+ }
+ if (stab("users", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users switch -m passwd");
+ (void) makemapentry(buf);
+ }
+#endif
+}
+ /*
+** SWITCH_MAP_FIND -- find the list of types associated with a map
+**
+** This is the system-dependent interface to the service switch.
+**
+** Parameters:
+** service -- the name of the service of interest.
+** maptype -- an out-array of strings containing the types
+** of access to use for this service. There can
+** be at most MAXMAPSTACK types for a single service.
+** mapreturn -- an out-array of return information bitmaps
+** for the map.
+**
+** Returns:
+** The number of map types filled in, or -1 for failure.
+*/
+
+#if defined(SOLARIS) || (defined(sony_news) && defined(__svr4))
+# define _USE_SUN_NSSWITCH_
+#endif
+
+#ifdef _USE_SUN_NSSWITCH_
+# include <nsswitch.h>
+#endif
+
+#if defined(ultrix) || (defined(__osf__) && defined(__alpha))
+# define _USE_DEC_SVC_CONF_
+#endif
+
+#ifdef _USE_DEC_SVC_CONF_
+# include <sys/svcinfo.h>
+#endif
+
+int
+switch_map_find(service, maptype, mapreturn)
+ char *service;
+ char *maptype[MAXMAPSTACK];
+ short mapreturn[MAXMAPACTIONS];
+{
+ int svcno;
+
+#ifdef _USE_SUN_NSSWITCH_
+ struct __nsw_switchconfig *nsw_conf;
+ enum __nsw_parse_err pserr;
+ struct __nsw_lookup *lk;
+ static struct __nsw_lookup lkp0 =
+ { "files", {1, 0, 0, 0}, NULL, NULL };
+ static struct __nsw_switchconfig lkp_default =
+ { 0, "sendmail", 3, &lkp0 };
+
+ for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
+ mapreturn[svcno] = 0;
+
+ if ((nsw_conf = __nsw_getconfig(service, &pserr)) == NULL)
+ lk = lkp_default.lookups;
+ else
+ lk = nsw_conf->lookups;
+ svcno = 0;
+ while (lk != NULL)
+ {
+ maptype[svcno] = lk->service_name;
+ if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN)
+ mapreturn[MA_NOTFOUND] |= 1 << svcno;
+ if (lk->actions[__NSW_TRYAGAIN] == __NSW_RETURN)
+ mapreturn[MA_TRYAGAIN] |= 1 << svcno;
+ if (lk->actions[__NSW_UNAVAIL] == __NSW_RETURN)
+ mapreturn[MA_TRYAGAIN] |= 1 << svcno;
+ svcno++;
+ lk = lk->next;
+ }
+ return svcno;
+#endif
+
+#ifdef _USE_DEC_SVC_CONF_
+ struct svcinfo *svcinfo;
+ int svc;
+
+ for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
+ mapreturn[svcno] = 0;
+
+ svcinfo = getsvc();
+ if (svcinfo == NULL)
+ goto punt;
+ if (strcmp(service, "hosts") == 0)
+ svc = SVC_HOSTS;
+ else if (strcmp(service, "aliases") == 0)
+ svc = SVC_ALIASES;
+ else if (strcmp(service, "passwd") == 0)
+ svc = SVC_PASSWD;
+ else
+ return -1;
+ for (svcno = 0; svcno < SVC_PATHSIZE; svcno++)
+ {
+ switch (svcinfo->svcpath[svc][svcno])
+ {
+ case SVC_LOCAL:
+ maptype[svcno] = "files";
+ break;
+
+ case SVC_YP:
+ maptype[svcno] = "nis";
+ break;
+
+ case SVC_BIND:
+ maptype[svcno] = "dns";
+ break;
+
+#ifdef SVC_HESIOD
+ case SVC_HESIOD:
+ maptype[svcno] = "hesiod";
+ break;
+#endif
+
+ case SVC_LAST:
+ return svcno;
+ }
+ }
+ return svcno;
+#endif
+
+#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
+ /*
+ ** Fall-back mechanism.
+ */
+
+ STAB *st;
+ time_t now = curtime();
+
+ for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
+ mapreturn[svcno] = 0;
+
+ if ((now - ServiceCacheTime) > (time_t) ServiceCacheMaxAge)
+ {
+ /* (re)read service switch */
+ register FILE *fp;
+ int sff = SFF_REGONLY|SFF_OPENASROOT|SFF_NOLOCK;
+
+ if (!bitset(DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR, DontBlameSendmail))
+ sff |= SFF_NOWLINK;
+
+ if (ConfigFileRead)
+ ServiceCacheTime = now;
+ fp = safefopen(ServiceSwitchFile, O_RDONLY, 0, sff);
+ if (fp != NULL)
+ {
+ char buf[MAXLINE];
+
+ while (fgets(buf, sizeof buf, fp) != NULL)
+ {
+ register char *p;
+
+ p = strpbrk(buf, "#\n");
+ if (p != NULL)
+ *p = '\0';
+ p = strpbrk(buf, " \t");
+ if (p != NULL)
+ *p++ = '\0';
+ if (buf[0] == '\0')
+ continue;
+ while (isspace(*p))
+ p++;
+ if (*p == '\0')
+ continue;
+
+ /*
+ ** Find/allocate space for this service entry.
+ ** Space for all of the service strings
+ ** are allocated at once. This means
+ ** that we only have to free the first
+ ** one to free all of them.
+ */
+
+ st = stab(buf, ST_SERVICE, ST_ENTER);
+ if (st->s_service[0] != NULL)
+ free((void *) st->s_service[0]);
+ p = newstr(p);
+ for (svcno = 0; svcno < MAXMAPSTACK; )
+ {
+ if (*p == '\0')
+ break;
+ st->s_service[svcno++] = p;
+ p = strpbrk(p, " \t");
+ if (p == NULL)
+ break;
+ *p++ = '\0';
+ while (isspace(*p))
+ p++;
+ }
+ if (svcno < MAXMAPSTACK)
+ st->s_service[svcno] = NULL;
+ }
+ fclose(fp);
+ }
+ }
+
+ /* look up entry in cache */
+ st = stab(service, ST_SERVICE, ST_FIND);
+ if (st != NULL && st->s_service[0] != NULL)
+ {
+ /* extract data */
+ svcno = 0;
+ while (svcno < MAXMAPSTACK)
+ {
+ maptype[svcno] = st->s_service[svcno];
+ if (maptype[svcno++] == NULL)
+ break;
+ }
+ return --svcno;
+ }
+#endif
+
+#if !defined(_USE_SUN_NSSWITCH_)
+ /* if the service file doesn't work, use an absolute fallback */
+# ifdef _USE_DEC_SVC_CONF_
+ punt:
+# endif
+ for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
+ mapreturn[svcno] = 0;
+ svcno = 0;
+ if (strcmp(service, "aliases") == 0)
+ {
+ maptype[svcno++] = "files";
+# ifdef AUTO_NIS_ALIASES
+# ifdef NISPLUS
+ maptype[svcno++] = "nisplus";
+# endif
+# ifdef NIS
+ maptype[svcno++] = "nis";
+# endif
+# endif
+ return svcno;
+ }
+ if (strcmp(service, "hosts") == 0)
+ {
+# if NAMED_BIND
+ maptype[svcno++] = "dns";
+# else
+# if defined(sun) && !defined(BSD)
+ /* SunOS */
+ maptype[svcno++] = "nis";
+# endif
+# endif
+ maptype[svcno++] = "files";
+ return svcno;
+ }
+ return -1;
+#endif
+}
+ /*
+** USERNAME -- return the user id of the logged in user.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** The login name of the logged in user.
+**
+** Side Effects:
+** none.
+**
+** Notes:
+** The return value is statically allocated.
+*/
+
+char *
+username()
+{
+ static char *myname = NULL;
+ extern char *getlogin();
+ register struct passwd *pw;
+
+ /* cache the result */
+ if (myname == NULL)
+ {
+ myname = getlogin();
+ if (myname == NULL || myname[0] == '\0')
+ {
+ pw = sm_getpwuid(RealUid);
+ if (pw != NULL)
+ myname = newstr(pw->pw_name);
+ }
+ else
+ {
+ uid_t uid = RealUid;
+
+ myname = newstr(myname);
+ if ((pw = sm_getpwnam(myname)) == NULL ||
+ (uid != 0 && uid != pw->pw_uid))
+ {
+ pw = sm_getpwuid(uid);
+ if (pw != NULL)
+ myname = newstr(pw->pw_name);
+ }
+ }
+ if (myname == NULL || myname[0] == '\0')
+ {
+ syserr("554 Who are you?");
+ myname = "postmaster";
+ }
+ }
+
+ return (myname);
+}
+ /*
+** TTYPATH -- Get the path of the user's tty
+**
+** Returns the pathname of the user's tty. Returns NULL if
+** the user is not logged in or if s/he has write permission
+** denied.
+**
+** Parameters:
+** none
+**
+** Returns:
+** pathname of the user's tty.
+** NULL if not logged in or write permission denied.
+**
+** Side Effects:
+** none.
+**
+** WARNING:
+** Return value is in a local buffer.
+**
+** Called By:
+** savemail
+*/
+
+char *
+ttypath()
+{
+ struct stat stbuf;
+ register char *pathn;
+ extern char *ttyname();
+ extern char *getlogin();
+
+ /* compute the pathname of the controlling tty */
+ if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
+ (pathn = ttyname(0)) == NULL)
+ {
+ errno = 0;
+ return (NULL);
+ }
+
+ /* see if we have write permission */
+ if (stat(pathn, &stbuf) < 0 || !bitset(S_IWOTH, stbuf.st_mode))
+ {
+ errno = 0;
+ return (NULL);
+ }
+
+ /* see if the user is logged in */
+ if (getlogin() == NULL)
+ return (NULL);
+
+ /* looks good */
+ return (pathn);
+}
+ /*
+** CHECKCOMPAT -- check for From and To person compatible.
+**
+** This routine can be supplied on a per-installation basis
+** to determine whether a person is allowed to send a message.
+** This allows restriction of certain types of internet
+** forwarding or registration of users.
+**
+** If the hosts are found to be incompatible, an error
+** message should be given using "usrerr" and an EX_ code
+** should be returned. You can also set to->q_status to
+** a DSN-style status code.
+**
+** EF_NO_BODY_RETN can be set in e->e_flags to suppress the
+** body during the return-to-sender function; this should be done
+** on huge messages. This bit may already be set by the ESMTP
+** protocol.
+**
+** Parameters:
+** to -- the person being sent to.
+**
+** Returns:
+** an exit status
+**
+** Side Effects:
+** none (unless you include the usrerr stuff)
+*/
+
+int
+checkcompat(to, e)
+ register ADDRESS *to;
+ register ENVELOPE *e;
+{
+# ifdef lint
+ if (to == NULL)
+ to++;
+# endif /* lint */
+
+ if (tTd(49, 1))
+ printf("checkcompat(to=%s, from=%s)\n",
+ to->q_paddr, e->e_from.q_paddr);
+
+# ifdef EXAMPLE_CODE
+ /* this code is intended as an example only */
+ register STAB *s;
+
+ s = stab("arpa", ST_MAILER, ST_FIND);
+ if (s != NULL && strcmp(e->e_from.q_mailer->m_name, "local") != 0 &&
+ to->q_mailer == s->s_mailer)
+ {
+ usrerr("553 No ARPA mail through this machine: see your system administration");
+ /* e->e_flags |= EF_NO_BODY_RETN; to supress body on return */
+ to->q_status = "5.7.1";
+ return (EX_UNAVAILABLE);
+ }
+# endif /* EXAMPLE_CODE */
+ return (EX_OK);
+}
+ /*
+** SETSIGNAL -- set a signal handler
+**
+** This is essentially old BSD "signal(3)".
+*/
+
+sigfunc_t
+setsignal(sig, handler)
+ int sig;
+ sigfunc_t handler;
+{
+#if defined(SYS5SIGNALS) || defined(BSD4_3)
+# ifdef BSD4_3
+ return signal(sig, handler);
+# else
+ return sigset(sig, handler);
+# endif
+#else
+ struct sigaction n, o;
+
+ bzero(&n, sizeof n);
+# if USE_SA_SIGACTION
+ n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler;
+ n.sa_flags = SA_RESTART|SA_SIGINFO;
+# else
+ n.sa_handler = handler;
+# ifdef SA_RESTART
+ n.sa_flags = SA_RESTART;
+# endif
+# endif
+ if (sigaction(sig, &n, &o) < 0)
+ return SIG_ERR;
+ return o.sa_handler;
+#endif
+}
+ /*
+** BLOCKSIGNAL -- hold a signal to prevent delivery
+**
+** Parameters:
+** sig -- the signal to block.
+**
+** Returns:
+** 1 signal was previously blocked
+** 0 signal was not previously blocked
+** -1 on failure.
+*/
+
+int
+blocksignal(sig)
+ int sig;
+{
+#ifdef BSD4_3
+# ifndef sigmask
+# define sigmask(s) (1 << ((s) - 1))
+# endif
+ return (sigblock(sigmask(sig)) & sigmask(sig)) != 0;
+#else
+# ifdef ALTOS_SYSTEM_V
+ sigfunc_t handler;
+
+ handler = sigset(sig, SIG_HOLD);
+ if (handler == SIG_ERR)
+ return -1;
+ else
+ return handler == SIG_HOLD;
+# else
+ sigset_t sset, oset;
+
+ sigemptyset(&sset);
+ sigaddset(&sset, sig);
+ if (sigprocmask(SIG_BLOCK, &sset, &oset) < 0)
+ return -1;
+ else
+ return sigismember(&oset, sig);
+# endif
+#endif
+}
+ /*
+** RELEASESIGNAL -- release a held signal
+**
+** Parameters:
+** sig -- the signal to release.
+**
+** Returns:
+** 1 signal was previously blocked
+** 0 signal was not previously blocked
+** -1 on failure.
+*/
+
+int
+releasesignal(sig)
+ int sig;
+{
+#ifdef BSD4_3
+ return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0;
+#else
+# ifdef ALTOS_SYSTEM_V
+ sigfunc_t handler;
+
+ handler = sigset(sig, SIG_HOLD);
+ if (sigrelse(sig) < 0)
+ return -1;
+ else
+ return handler == SIG_HOLD;
+# else
+ sigset_t sset, oset;
+
+ sigemptyset(&sset);
+ sigaddset(&sset, sig);
+ if (sigprocmask(SIG_UNBLOCK, &sset, &oset) < 0)
+ return -1;
+ else
+ return sigismember(&oset, sig);
+# endif
+#endif
+}
+ /*
+** HOLDSIGS -- arrange to hold all signals
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Arranges that signals are held.
+*/
+
+void
+holdsigs()
+{
+}
+ /*
+** RLSESIGS -- arrange to release all signals
+**
+** This undoes the effect of holdsigs.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Arranges that signals are released.
+*/
+
+void
+rlsesigs()
+{
+}
+ /*
+** INIT_MD -- do machine dependent initializations
+**
+** Systems that have global modes that should be set should do
+** them here rather than in main.
+*/
+
+#ifdef _AUX_SOURCE
+# include <compat.h>
+#endif
+
+#if SHARE_V1
+# include <shares.h>
+#endif
+
+void
+init_md(argc, argv)
+ int argc;
+ char **argv;
+{
+#ifdef _AUX_SOURCE
+ setcompat(getcompat() | COMPAT_BSDPROT);
+#endif
+
+#ifdef SUN_EXTENSIONS
+ init_md_sun();
+#endif
+
+#if _CONVEX_SOURCE
+ /* keep gethostby*() from stripping the local domain name */
+ set_domain_trim_off();
+#endif
+#ifdef __QNX__
+ /*
+ ** Due to QNX's network distributed nature, you can target a tcpip
+ ** stack on a different node in the qnx network; this patch lets
+ ** this feature work. The __sock_locate() must be done before the
+ ** environment is clear.
+ */
+ __sock_locate();
+#endif
+#if SECUREWARE || defined(_SCO_unix_)
+ set_auth_parameters(argc, argv);
+
+# ifdef _SCO_unix_
+ /*
+ ** This is required for highest security levels (the kernel
+ ** won't let it call set*uid() or run setuid binaries without
+ ** it). It may be necessary on other SECUREWARE systems.
+ */
+
+ if (getluid() == -1)
+ setluid(0);
+# endif
+#endif
+
+#ifdef VENDOR_DEFAULT
+ VendorCode = VENDOR_DEFAULT;
+#else
+ VendorCode = VENDOR_BERKELEY;
+#endif
+}
+ /*
+** INIT_VENDOR_MACROS -- vendor-dependent macro initializations
+**
+** Called once, on startup.
+**
+** Parameters:
+** e -- the global envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** vendor-dependent.
+*/
+
+void
+init_vendor_macros(e)
+ register ENVELOPE *e;
+{
+}
+ /*
+** GETLA -- get the current load average
+**
+** This code stolen from la.c.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** The current load average as an integer.
+**
+** Side Effects:
+** none.
+*/
+
+/* try to guess what style of load average we have */
+#define LA_ZERO 1 /* always return load average as zero */
+#define LA_INT 2 /* read kmem for avenrun; interpret as long */
+#define LA_FLOAT 3 /* read kmem for avenrun; interpret as float */
+#define LA_SUBR 4 /* call getloadavg */
+#define LA_MACH 5 /* MACH load averages (as on NeXT boxes) */
+#define LA_SHORT 6 /* read kmem for avenrun; interpret as short */
+#define LA_PROCSTR 7 /* read string ("1.17") from /proc/loadavg */
+#define LA_READKSYM 8 /* SVR4: use MIOC_READKSYM ioctl call */
+#define LA_DGUX 9 /* special DGUX implementation */
+#define LA_HPUX 10 /* special HPUX implementation */
+#define LA_IRIX6 11 /* special IRIX 6.2 implementation */
+#define LA_KSTAT 12 /* special Solaris kstat(3k) implementation */
+#define LA_DEVSHORT 13 /* read short from a device */
+#define LA_ALPHAOSF 14 /* Digital UNIX (OSF/1 on Alpha) table() call */
+
+/* do guesses based on general OS type */
+#ifndef LA_TYPE
+# define LA_TYPE LA_ZERO
+#endif
+
+#ifndef FSHIFT
+# if defined(unixpc)
+# define FSHIFT 5
+# endif
+
+# if defined(__alpha) || defined(IRIX)
+# define FSHIFT 10
+# endif
+
+#endif
+
+#ifndef FSHIFT
+# define FSHIFT 8
+#endif
+
+#ifndef FSCALE
+# define FSCALE (1 << FSHIFT)
+#endif
+
+#ifndef LA_AVENRUN
+# ifdef SYSTEM5
+# define LA_AVENRUN "avenrun"
+# else
+# define LA_AVENRUN "_avenrun"
+# endif
+#endif
+
+/* _PATH_KMEM should be defined in <paths.h> */
+#ifndef _PATH_KMEM
+# define _PATH_KMEM "/dev/kmem"
+#endif
+
+#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT)
+
+#include <nlist.h>
+
+/* _PATH_UNIX should be defined in <paths.h> */
+#ifndef _PATH_UNIX
+# if defined(SYSTEM5)
+# define _PATH_UNIX "/unix"
+# else
+# define _PATH_UNIX "/vmunix"
+# endif
+#endif
+
+#ifdef _AUX_SOURCE
+struct nlist Nl[2];
+#else
+struct nlist Nl[] =
+{
+ { LA_AVENRUN },
+ { 0 },
+};
+#endif
+#define X_AVENRUN 0
+
+int
+getla()
+{
+ static int kmem = -1;
+#if LA_TYPE == LA_INT
+ long avenrun[3];
+#else
+# if LA_TYPE == LA_SHORT
+ short avenrun[3];
+# else
+ double avenrun[3];
+# endif
+#endif
+ extern int errno;
+ extern off_t lseek();
+
+ if (kmem < 0)
+ {
+#ifdef _AUX_SOURCE
+ strcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN);
+ Nl[1].n_name[0] = '\0';
+#endif
+
+#if defined(_AIX3) || defined(_AIX4)
+ if (knlist(Nl, 1, sizeof Nl[0]) < 0)
+#else
+ if (nlist(_PATH_UNIX, Nl) < 0)
+#endif
+ {
+ if (tTd(3, 1))
+ printf("getla: nlist(%s): %s\n", _PATH_UNIX,
+ errstring(errno));
+ return (-1);
+ }
+ if (Nl[X_AVENRUN].n_value == 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: nlist(%s, %s) ==> 0\n",
+ _PATH_UNIX, LA_AVENRUN);
+ return (-1);
+ }
+#ifdef NAMELISTMASK
+ Nl[X_AVENRUN].n_value &= NAMELISTMASK;
+#endif
+
+ kmem = open(_PATH_KMEM, 0, 0);
+ if (kmem < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: open(/dev/kmem): %s\n",
+ errstring(errno));
+ return (-1);
+ }
+ (void) fcntl(kmem, F_SETFD, 1);
+ }
+ if (tTd(3, 20))
+ printf("getla: symbol address = %#lx\n",
+ (u_long) Nl[X_AVENRUN].n_value);
+ if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 ||
+ read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
+ {
+ /* thank you Ian */
+ if (tTd(3, 1))
+ printf("getla: lseek or read: %s\n", errstring(errno));
+ return (-1);
+ }
+# if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT)
+ if (tTd(3, 5))
+ {
+# if LA_TYPE == LA_SHORT
+ printf("getla: avenrun = %d", avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %d, %d", avenrun[1], avenrun[2]);
+# else
+ printf("getla: avenrun = %ld", avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %ld, %ld", avenrun[1], avenrun[2]);
+# endif
+ printf("\n");
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+ return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+# else /* LA_TYPE == LA_FLOAT */
+ if (tTd(3, 5))
+ {
+ printf("getla: avenrun = %g", avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %g, %g", avenrun[1], avenrun[2]);
+ printf("\n");
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] +0.5));
+ return ((int) (avenrun[0] + 0.5));
+# endif
+}
+
+#endif /* LA_TYPE == LA_INT or LA_SHORT or LA_FLOAT */
+
+#if LA_TYPE == LA_READKSYM
+
+# include <sys/ksym.h>
+
+getla()
+{
+ static int kmem = -1;
+ long avenrun[3];
+ extern int errno;
+ struct mioc_rksym mirk;
+
+ if (kmem < 0)
+ {
+ kmem = open("/dev/kmem", 0, 0);
+ if (kmem < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: open(/dev/kmem): %s\n",
+ errstring(errno));
+ return (-1);
+ }
+ (void) fcntl(kmem, F_SETFD, 1);
+ }
+ mirk.mirk_symname = LA_AVENRUN;
+ mirk.mirk_buf = avenrun;
+ mirk.mirk_buflen = sizeof(avenrun);
+ if (ioctl(kmem, MIOC_READKSYM, &mirk) < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: ioctl(MIOC_READKSYM) failed: %s\n",
+ errstring(errno));
+ return -1;
+ }
+ if (tTd(3, 5))
+ {
+ printf("getla: avenrun = %d", avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %d, %d", avenrun[1], avenrun[2]);
+ printf("\n");
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+ return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+}
+
+#endif /* LA_TYPE == LA_READKSYM */
+
+#if LA_TYPE == LA_DGUX
+
+# include <sys/dg_sys_info.h>
+
+int
+getla()
+{
+ struct dg_sys_info_load_info load_info;
+
+ dg_sys_info((long *)&load_info,
+ DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0);
+
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (load_info.one_minute + 0.5));
+
+ return((int) (load_info.one_minute + 0.5));
+}
+
+#endif /* LA_TYPE == LA_DGUX */
+
+#if LA_TYPE == LA_HPUX
+
+/* forward declarations to keep gcc from complaining */
+struct pst_dynamic;
+struct pst_status;
+struct pst_static;
+struct pst_vminfo;
+struct pst_diskinfo;
+struct pst_processor;
+struct pst_lv;
+struct pst_swapinfo;
+
+# include <sys/param.h>
+# include <sys/pstat.h>
+
+int
+getla()
+{
+ struct pst_dynamic pstd;
+
+ if (pstat_getdynamic(&pstd, sizeof(struct pst_dynamic),
+ (size_t) 1, 0) == -1)
+ return 0;
+
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5));
+
+ return (int) (pstd.psd_avg_1_min + 0.5);
+}
+
+#endif /* LA_TYPE == LA_HPUX */
+
+#if LA_TYPE == LA_SUBR
+
+int
+getla()
+{
+ double avenrun[3];
+
+ if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0)
+ {
+ if (tTd(3, 1))
+ perror("getla: getloadavg failed:");
+ return (-1);
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] +0.5));
+ return ((int) (avenrun[0] + 0.5));
+}
+
+#endif /* LA_TYPE == LA_SUBR */
+
+#if LA_TYPE == LA_MACH
+
+/*
+** This has been tested on NEXTSTEP release 2.1/3.X.
+*/
+
+#if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0
+# include <mach/mach.h>
+#else
+# include <mach.h>
+#endif
+
+int
+getla()
+{
+ processor_set_t default_set;
+ kern_return_t error;
+ unsigned int info_count;
+ struct processor_set_basic_info info;
+ host_t host;
+
+ error = processor_set_default(host_self(), &default_set);
+ if (error != KERN_SUCCESS)
+ {
+ if (tTd(3, 1))
+ perror("getla: processor_set_default failed:");
+ return -1;
+ }
+ info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
+ if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO,
+ &host, (processor_set_info_t)&info,
+ &info_count) != KERN_SUCCESS)
+ {
+ if (tTd(3, 1))
+ perror("getla: processor_set_info failed:");
+ return -1;
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE);
+ return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE;
+}
+
+#endif /* LA_TYPE == LA_MACH */
+
+#if LA_TYPE == LA_PROCSTR
+
+/*
+** Read /proc/loadavg for the load average. This is assumed to be
+** in a format like "0.15 0.12 0.06".
+**
+** Initially intended for Linux. This has been in the kernel
+** since at least 0.99.15.
+*/
+
+# ifndef _PATH_LOADAVG
+# define _PATH_LOADAVG "/proc/loadavg"
+# endif
+
+int
+getla()
+{
+ double avenrun;
+ register int result;
+ FILE *fp;
+
+ fp = fopen(_PATH_LOADAVG, "r");
+ if (fp == NULL)
+ {
+ if (tTd(3, 1))
+ printf("getla: fopen(%s): %s\n",
+ _PATH_LOADAVG, errstring(errno));
+ return -1;
+ }
+ result = fscanf(fp, "%lf", &avenrun);
+ fclose(fp);
+ if (result != 1)
+ {
+ if (tTd(3, 1))
+ printf("getla: fscanf() = %d: %s\n",
+ result, errstring(errno));
+ return -1;
+ }
+
+ if (tTd(3, 1))
+ printf("getla(): %.2f\n", avenrun);
+
+ return ((int) (avenrun + 0.5));
+}
+
+#endif /* LA_TYPE == LA_PROCSTR */
+
+#if LA_TYPE == LA_IRIX6
+#include <sys/sysmp.h>
+
+int getla(void)
+{
+ static int kmem = -1;
+ int avenrun[3];
+
+ if (kmem < 0)
+ {
+ kmem = open(_PATH_KMEM, 0, 0);
+ if (kmem < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: open(%s): %s\n", _PATH_KMEM,
+ errstring(errno));
+ return -1;
+ }
+ (void) fcntl(kmem, F_SETFD, 1);
+ }
+
+ if (lseek(kmem, (sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff), SEEK_SET) == -1 ||
+ read(kmem, (char *)avenrun, sizeof(avenrun)) < sizeof(avenrun))
+ {
+ if (tTd(3, 1))
+ printf("getla: lseek or read: %s\n",
+ errstring(errno));
+ return -1;
+ }
+ if (tTd(3, 5))
+ {
+ printf("getla: avenrun = %ld", (long int) avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %ld, %ld",
+ (long int) avenrun[1], (long int) avenrun[2]);
+ printf("\n");
+ }
+
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+ return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+
+}
+#endif
+
+#if LA_TYPE == LA_KSTAT
+
+#include <kstat.h>
+
+int
+getla()
+{
+ static kstat_ctl_t *kc = NULL;
+ static kstat_t *ksp = NULL;
+ kstat_named_t *ksn;
+ int la;
+
+ if (kc == NULL) /* if not initialized before */
+ kc = kstat_open();
+ if (kc == NULL)
+ {
+ if (tTd(3, 1))
+ printf("getla: kstat_open(): %s\n",
+ errstring(errno));
+ return -1;
+ }
+ if (ksp == NULL)
+ ksp = kstat_lookup(kc, "unix", 0, "system_misc");
+ if (ksp == NULL)
+ {
+ if (tTd(3, 1))
+ printf("getla: kstat_lookup(): %s\n",
+ errstring(errno));
+ return -1;
+ }
+ if (kstat_read(kc, ksp, NULL) < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: kstat_read(): %s\n",
+ errstring(errno));
+ return -1;
+ }
+ ksn = (kstat_named_t *) kstat_data_lookup(ksp, "avenrun_1min");
+ la = ((double)ksn->value.ul + FSCALE/2) / FSCALE;
+ /* kstat_close(kc); /o do not close for fast access */
+ return la;
+}
+
+#endif /* LA_TYPE == LA_KSTAT */
+
+#if LA_TYPE == LA_DEVSHORT
+
+/*
+** Read /dev/table/avenrun for the load average. This should contain
+** three shorts for the 1, 5, and 15 minute loads. We only read the
+** first, since that's all we care about.
+**
+** Intended for SCO OpenServer 5.
+*/
+
+# ifndef _PATH_AVENRUN
+# define _PATH_AVENRUN "/dev/table/avenrun"
+# endif
+
+int
+getla()
+{
+ static int afd = -1;
+ short avenrun;
+ int loadav;
+ int r;
+
+ errno = EBADF;
+
+ if (afd == -1 || lseek(afd, 0L, SEEK_SET) == -1)
+ {
+ if (errno != EBADF)
+ return -1;
+ afd = open(_PATH_AVENRUN, O_RDONLY|O_SYNC);
+ if (afd < 0)
+ {
+ sm_syslog(LOG_ERR, NOQID,
+ "can't open %s: %m",
+ _PATH_AVENRUN);
+ return -1;
+ }
+ }
+
+ r = read(afd, &avenrun, sizeof avenrun);
+
+ if (tTd(3, 5))
+ printf("getla: avenrun = %d\n", avenrun);
+ loadav = (int) (avenrun + FSCALE/2) >> FSHIFT;
+ if (tTd(3, 1))
+ printf("getla: %d\n", loadav);
+ return loadav;
+}
+
+#endif /* LA_TYPE == LA_DEVSHORT */
+
+#if LA_TYPE == LA_ALPHAOSF
+struct rtentry;
+struct mbuf;
+# include <sys/table.h>
+
+int getla()
+{
+ int ave = 0;
+ struct tbl_loadavg tab;
+
+ if (table(TBL_LOADAVG, 0, &tab, 1, sizeof(tab)) == -1)
+ {
+ if (tTd(3, 1))
+ printf("getla: table %s\n", errstring(errno));
+ return (-1);
+ }
+
+ if (tTd(3, 1))
+ printf("getla: scale = %d\n", tab.tl_lscale);
+
+ if (tab.tl_lscale)
+ ave = (tab.tl_avenrun.l[0] + (tab.tl_lscale/2)) / tab.tl_lscale;
+ else
+ ave = (int) (tab.tl_avenrun.d[0] + 0.5);
+
+ if (tTd(3, 1))
+ printf("getla: %d\n", ave);
+
+ return ave;
+}
+
+#endif
+
+#if LA_TYPE == LA_ZERO
+
+int
+getla()
+{
+ if (tTd(3, 1))
+ printf("getla: ZERO\n");
+ return (0);
+}
+
+#endif /* LA_TYPE == LA_ZERO */
+
+/*
+ * Copyright 1989 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. M.I.T. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Many and varied...
+ */
+
+/* Non Apollo stuff removed by Don Lewis 11/15/93 */
+#ifndef lint
+static char rcsid[] = "@(#)$Id: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $";
+#endif /* !lint */
+
+#ifdef apollo
+# undef volatile
+# include <apollo/base.h>
+
+/* ARGSUSED */
+int getloadavg( call_data )
+ caddr_t call_data; /* pointer to (double) return value */
+{
+ double *avenrun = (double *) call_data;
+ int i;
+ status_$t st;
+ long loadav[3];
+ proc1_$get_loadav(loadav, &st);
+ *avenrun = loadav[0] / (double) (1 << 16);
+ return(0);
+}
+# endif /* apollo */
+ /*
+** SHOULDQUEUE -- should this message be queued or sent?
+**
+** Compares the message cost to the load average to decide.
+**
+** Parameters:
+** pri -- the priority of the message in question.
+** ctime -- the message creation time.
+**
+** Returns:
+** TRUE -- if this message should be queued up for the
+** time being.
+** FALSE -- if the load is low enough to send this message.
+**
+** Side Effects:
+** none.
+*/
+
+extern int get_num_procs_online __P((void));
+
+bool
+shouldqueue(pri, ctime)
+ long pri;
+ time_t ctime;
+{
+ bool rval;
+ int queuela = QueueLA * get_num_procs_online();
+
+ if (tTd(3, 30))
+ printf("shouldqueue: CurrentLA=%d, pri=%ld: ", CurrentLA, pri);
+ if (CurrentLA < queuela)
+ {
+ if (tTd(3, 30))
+ printf("FALSE (CurrentLA < QueueLA)\n");
+ return (FALSE);
+ }
+#if 0 /* this code is reported to cause oscillation around RefuseLA */
+ if (CurrentLA >= RefuseLA && QueueLA < RefuseLA)
+ {
+ if (tTd(3, 30))
+ printf("TRUE (CurrentLA >= RefuseLA)\n");
+ return (TRUE);
+ }
+#endif
+ rval = pri > (QueueFactor / (CurrentLA - queuela + 1));
+ if (tTd(3, 30))
+ printf("%s (by calculation)\n", rval ? "TRUE" : "FALSE");
+ return rval;
+}
+ /*
+** REFUSECONNECTIONS -- decide if connections should be refused
+**
+** Parameters:
+** port -- port number (for error messages only)
+**
+** Returns:
+** TRUE if incoming SMTP connections should be refused
+** (for now).
+** FALSE if we should accept new work.
+**
+** Side Effects:
+** Sets process title when it is rejecting connections.
+*/
+
+bool
+refuseconnections(port)
+ int port;
+{
+ int refusela = RefuseLA * get_num_procs_online();
+ time_t now;
+ static time_t lastconn = (time_t) 0;
+ static int conncnt = 0;
+ extern bool enoughdiskspace __P((long));
+
+#ifdef XLA
+ if (!xla_smtp_ok())
+ return TRUE;
+#endif
+
+ now = curtime();
+ if (now != lastconn)
+ {
+ lastconn = now;
+ conncnt = 0;
+ }
+ else if (conncnt++ > ConnRateThrottle && ConnRateThrottle > 0)
+ {
+ /* sleep to flatten out connection load */
+ setproctitle("deferring connections on port %d: %d per second",
+ port, ConnRateThrottle);
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "deferring connections on port %d: %d per second",
+ port, ConnRateThrottle);
+ sleep(1);
+ }
+
+ CurrentLA = getla();
+ if (CurrentLA >= refusela)
+ {
+ setproctitle("rejecting connections on port %d: load average: %d",
+ port, CurrentLA);
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "rejecting connections on port %d: load average: %d",
+ port, CurrentLA);
+ return TRUE;
+ }
+
+ if (!enoughdiskspace(MinBlocksFree + 1))
+ {
+ setproctitle("rejecting connections on port %d: min free: %d",
+ port, MinBlocksFree);
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "rejecting connections on port %d: min free: %d",
+ port, MinBlocksFree);
+ return TRUE;
+ }
+
+ if (MaxChildren > 0 && CurChildren >= MaxChildren)
+ {
+ extern void proc_list_probe __P((void));
+
+ proc_list_probe();
+ if (CurChildren >= MaxChildren)
+ {
+ setproctitle("rejecting connections on port %d: %d children, max %d",
+ port, CurChildren, MaxChildren);
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "rejecting connections on port %d: %d children, max %d",
+ port, CurChildren, MaxChildren);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+ /*
+** SETPROCTITLE -- set process title for ps
+**
+** Parameters:
+** fmt -- a printf style format string.
+** a, b, c -- possible parameters to fmt.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Clobbers argv of our main procedure so ps(1) will
+** display the title.
+*/
+
+#define SPT_NONE 0 /* don't use it at all */
+#define SPT_REUSEARGV 1 /* cover argv with title information */
+#define SPT_BUILTIN 2 /* use libc builtin */
+#define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */
+#define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */
+#define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */
+#define SPT_SCO 6 /* write kernel u. area */
+#define SPT_CHANGEARGV 7 /* write our own strings into argv[] */
+
+#ifndef SPT_TYPE
+# define SPT_TYPE SPT_REUSEARGV
+#endif
+
+#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
+
+# if SPT_TYPE == SPT_PSTAT
+# include <sys/pstat.h>
+# endif
+# if SPT_TYPE == SPT_PSSTRINGS
+# include <machine/vmparam.h>
+# include <sys/exec.h>
+# ifndef PS_STRINGS /* hmmmm.... apparently not available after all */
+# undef SPT_TYPE
+# define SPT_TYPE SPT_REUSEARGV
+# else
+# ifndef NKPDE /* FreeBSD 2.0 */
+# define NKPDE 63
+typedef unsigned int *pt_entry_t;
+# endif
+# endif
+# endif
+
+# if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
+# define SETPROC_STATIC static
+# else
+# define SETPROC_STATIC
+# endif
+
+# if SPT_TYPE == SPT_SYSMIPS
+# include <sys/sysmips.h>
+# include <sys/sysnews.h>
+# endif
+
+# if SPT_TYPE == SPT_SCO
+# include <sys/immu.h>
+# include <sys/dir.h>
+# include <sys/user.h>
+# include <sys/fs/s5param.h>
+# if PSARGSZ > MAXLINE
+# define SPT_BUFSIZE PSARGSZ
+# endif
+# endif
+
+# ifndef SPT_PADCHAR
+# define SPT_PADCHAR ' '
+# endif
+
+# ifndef SPT_BUFSIZE
+# define SPT_BUFSIZE MAXLINE
+# endif
+
+#endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
+
+/*
+** Pointers for setproctitle.
+** This allows "ps" listings to give more useful information.
+*/
+
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArgv = NULL; /* end of argv */
+
+void
+initsetproctitle(argc, argv, envp)
+ int argc;
+ char **argv;
+ char **envp;
+{
+ register int i, envpsize = 0;
+ extern char **environ;
+
+ /*
+ ** Move the environment so setproctitle can use the space at
+ ** the top of memory.
+ */
+
+ for (i = 0; envp[i] != NULL; i++)
+ envpsize += strlen(envp[i]) + 1;
+ environ = (char **) xalloc(sizeof (char *) * (i + 1));
+ for (i = 0; envp[i] != NULL; i++)
+ environ[i] = newstr(envp[i]);
+ environ[i] = NULL;
+
+ /*
+ ** Save start and extent of argv for setproctitle.
+ */
+
+ Argv = argv;
+
+ /*
+ ** Determine how much space we can use for setproctitle.
+ ** Use all contiguous argv and envp pointers starting at argv[0]
+ */
+ for (i = 0; i < argc; i++)
+ {
+ if (i==0 || LastArgv + 1 == argv[i])
+ LastArgv = argv[i] + strlen(argv[i]);
+ else
+ continue;
+ }
+ for (i=0; envp[i] != NULL; i++)
+ {
+ if (LastArgv + 1 == envp[i])
+ LastArgv = envp[i] + strlen(envp[i]);
+ else
+ continue;
+ }
+}
+
+#if SPT_TYPE != SPT_BUILTIN
+
+
+/*VARARGS1*/
+void
+# ifdef __STDC__
+setproctitle(const char *fmt, ...)
+# else
+setproctitle(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+# endif
+{
+# if SPT_TYPE != SPT_NONE
+ register char *p;
+ register int i;
+ SETPROC_STATIC char buf[SPT_BUFSIZE];
+ VA_LOCAL_DECL
+# if SPT_TYPE == SPT_PSTAT
+ union pstun pst;
+# endif
+# if SPT_TYPE == SPT_SCO
+ off_t seek_off;
+ static int kmem = -1;
+ static int kmempid = -1;
+ struct user u;
+# endif
+
+ p = buf;
+
+ /* print sendmail: heading for grep */
+ (void) strcpy(p, "sendmail: ");
+ p += strlen(p);
+
+ /* print the argument string */
+ VA_START(fmt);
+ (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
+ VA_END;
+
+ i = strlen(buf);
+
+# if SPT_TYPE == SPT_PSTAT
+ pst.pst_command = buf;
+ pstat(PSTAT_SETCMD, pst, i, 0, 0);
+# endif
+# if SPT_TYPE == SPT_PSSTRINGS
+ PS_STRINGS->ps_nargvstr = 1;
+ PS_STRINGS->ps_argvstr = buf;
+# endif
+# if SPT_TYPE == SPT_SYSMIPS
+ sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
+# endif
+# if SPT_TYPE == SPT_SCO
+ if (kmem < 0 || kmempid != getpid())
+ {
+ if (kmem >= 0)
+ close(kmem);
+ kmem = open(_PATH_KMEM, O_RDWR, 0);
+ if (kmem < 0)
+ return;
+ (void) fcntl(kmem, F_SETFD, 1);
+ kmempid = getpid();
+ }
+ buf[PSARGSZ - 1] = '\0';
+ seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u;
+ if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off)
+ (void) write(kmem, buf, PSARGSZ);
+# endif
+# if SPT_TYPE == SPT_REUSEARGV
+ if (i > LastArgv - Argv[0] - 2)
+ {
+ i = LastArgv - Argv[0] - 2;
+ buf[i] = '\0';
+ }
+ (void) strcpy(Argv[0], buf);
+ p = &Argv[0][i];
+ while (p < LastArgv)
+ *p++ = SPT_PADCHAR;
+ Argv[1] = NULL;
+# endif
+# if SPT_TYPE == SPT_CHANGEARGV
+ Argv[0] = buf;
+ Argv[1] = 0;
+# endif
+# endif /* SPT_TYPE != SPT_NONE */
+}
+
+#endif /* SPT_TYPE != SPT_BUILTIN */
+ /*
+** WAITFOR -- wait for a particular process id.
+**
+** Parameters:
+** pid -- process id to wait for.
+**
+** Returns:
+** status of pid.
+** -1 if pid never shows up.
+**
+** Side Effects:
+** none.
+*/
+
+int
+waitfor(pid)
+ pid_t pid;
+{
+#ifdef WAITUNION
+ union wait st;
+#else
+ auto int st;
+#endif
+ pid_t i;
+#if defined(ISC_UNIX) || defined(_SCO_unix_)
+ int savesig;
+#endif
+
+ do
+ {
+ errno = 0;
+#if defined(ISC_UNIX) || defined(_SCO_unix_)
+ savesig = releasesignal(SIGCHLD);
+#endif
+ i = wait(&st);
+#if defined(ISC_UNIX) || defined(_SCO_unix_)
+ if (savesig > 0)
+ blocksignal(SIGCHLD);
+#endif
+ if (i > 0)
+ proc_list_drop(i);
+ } while ((i >= 0 || errno == EINTR) && i != pid);
+ if (i < 0)
+ return -1;
+#ifdef WAITUNION
+ return st.w_status;
+#else
+ return st;
+#endif
+}
+ /*
+** REAPCHILD -- pick up the body of my child, lest it become a zombie
+**
+** Parameters:
+** sig -- the signal that got us here (unused).
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Picks up extant zombies.
+*/
+
+SIGFUNC_DECL
+reapchild(sig)
+ int sig;
+{
+ int olderrno = errno;
+ pid_t pid;
+# ifdef HASWAITPID
+ auto int status;
+ int count;
+
+ count = 0;
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+ {
+ if (count++ > 1000)
+ {
+ if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, NOQID,
+ "reapchild: waitpid loop: pid=%d, status=%x",
+ pid, status);
+ break;
+ }
+ proc_list_drop(pid);
+ }
+# else
+# ifdef WNOHANG
+ union wait status;
+
+ while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0)
+ proc_list_drop(pid);
+# else /* WNOHANG */
+ auto int status;
+
+ /*
+ ** Catch one zombie -- we will be re-invoked (we hope) if there
+ ** are more. Unreliable signals probably break this, but this
+ ** is the "old system" situation -- waitpid or wait3 are to be
+ ** strongly preferred.
+ */
+
+ if ((pid = wait(&status)) > 0)
+ proc_list_drop(pid);
+# endif /* WNOHANG */
+# endif
+# ifdef SYS5SIGNALS
+ (void) setsignal(SIGCHLD, reapchild);
+# endif
+ errno = olderrno;
+ return SIGFUNC_RETURN;
+}
+ /*
+** PUTENV -- emulation of putenv() in terms of setenv()
+**
+** Not needed on Posix-compliant systems.
+** This doesn't have full Posix semantics, but it's good enough
+** for sendmail.
+**
+** Parameter:
+** env -- the environment to put.
+**
+** Returns:
+** none.
+*/
+
+#ifdef NEEDPUTENV
+
+# if NEEDPUTENV == 2 /* no setenv(3) call available */
+
+int
+putenv(str)
+ char *str;
+{
+ char **current;
+ int matchlen, envlen=0;
+ char *tmp;
+ char **newenv;
+ static int first=1;
+ extern char **environ;
+
+ /*
+ * find out how much of str to match when searching
+ * for a string to replace.
+ */
+ if ((tmp = strchr(str, '=')) == NULL || tmp == str)
+ matchlen = strlen(str);
+ else
+ matchlen = (int) (tmp - str);
+ ++matchlen;
+
+ /*
+ * Search for an existing string in the environment and find the
+ * length of environ. If found, replace and exit.
+ */
+ for (current=environ; *current; current++) {
+ ++envlen;
+
+ if (strncmp(str, *current, matchlen) == 0) {
+ /* found it, now insert the new version */
+ *current = (char *)str;
+ return(0);
+ }
+ }
+
+ /*
+ * There wasn't already a slot so add space for a new slot.
+ * If this is our first time through, use malloc(), else realloc().
+ */
+ if (first) {
+ newenv = (char **) malloc(sizeof(char *) * (envlen + 2));
+ if (newenv == NULL)
+ return(-1);
+
+ first=0;
+ (void) memcpy(newenv, environ, sizeof(char *) * envlen);
+ } else {
+ newenv = (char **) realloc((char *)environ, sizeof(char *) * (envlen + 2));
+ if (newenv == NULL)
+ return(-1);
+ }
+
+ /* actually add in the new entry */
+ environ = newenv;
+ environ[envlen] = (char *)str;
+ environ[envlen+1] = NULL;
+
+ return(0);
+}
+
+#else /* implement putenv() in terms of setenv() */
+
+int
+putenv(env)
+ char *env;
+{
+ char *p;
+ int l;
+ char nbuf[100];
+
+ p = strchr(env, '=');
+ if (p == NULL)
+ return 0;
+ l = p - env;
+ if (l > sizeof nbuf - 1)
+ l = sizeof nbuf - 1;
+ bcopy(env, nbuf, l);
+ nbuf[l] = '\0';
+ return setenv(nbuf, ++p, 1);
+}
+
+# endif
+#endif
+ /*
+** UNSETENV -- remove a variable from the environment
+**
+** Not needed on newer systems.
+**
+** Parameters:
+** name -- the string name of the environment variable to be
+** deleted from the current environment.
+**
+** Returns:
+** none.
+**
+** Globals:
+** environ -- a pointer to the current environment.
+**
+** Side Effects:
+** Modifies environ.
+*/
+
+#ifndef HASUNSETENV
+
+void
+unsetenv(name)
+ char *name;
+{
+ extern char **environ;
+ register char **pp;
+ int len = strlen(name);
+
+ for (pp = environ; *pp != NULL; pp++)
+ {
+ if (strncmp(name, *pp, len) == 0 &&
+ ((*pp)[len] == '=' || (*pp)[len] == '\0'))
+ break;
+ }
+
+ for (; *pp != NULL; pp++)
+ *pp = pp[1];
+}
+
+#endif
+ /*
+** GETDTABLESIZE -- return number of file descriptors
+**
+** Only on non-BSD systems
+**
+** Parameters:
+** none
+**
+** Returns:
+** size of file descriptor table
+**
+** Side Effects:
+** none
+*/
+
+#ifdef SOLARIS
+# include <sys/resource.h>
+#endif
+
+int
+getdtsize()
+{
+#ifdef RLIMIT_NOFILE
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
+ return rl.rlim_cur;
+#endif
+
+# ifdef HASGETDTABLESIZE
+ return getdtablesize();
+# else
+# ifdef _SC_OPEN_MAX
+ return sysconf(_SC_OPEN_MAX);
+# else
+ return NOFILE;
+# endif
+# endif
+}
+ /*
+** UNAME -- get the UUCP name of this system.
+*/
+
+#ifndef HASUNAME
+
+int
+uname(name)
+ struct utsname *name;
+{
+ FILE *file;
+ char *n;
+
+ name->nodename[0] = '\0';
+
+ /* try /etc/whoami -- one line with the node name */
+ if ((file = fopen("/etc/whoami", "r")) != NULL)
+ {
+ (void) fgets(name->nodename, NODE_LENGTH + 1, file);
+ (void) fclose(file);
+ n = strchr(name->nodename, '\n');
+ if (n != NULL)
+ *n = '\0';
+ if (name->nodename[0] != '\0')
+ return (0);
+ }
+
+ /* try /usr/include/whoami.h -- has a #define somewhere */
+ if ((file = fopen("/usr/include/whoami.h", "r")) != NULL)
+ {
+ char buf[MAXLINE];
+
+ while (fgets(buf, MAXLINE, file) != NULL)
+ if (sscanf(buf, "#define sysname \"%*[^\"]\"",
+ NODE_LENGTH, name->nodename) > 0)
+ break;
+ (void) fclose(file);
+ if (name->nodename[0] != '\0')
+ return (0);
+ }
+
+#ifdef TRUST_POPEN
+ /*
+ ** Popen is known to have security holes.
+ */
+
+ /* try uuname -l to return local name */
+ if ((file = popen("uuname -l", "r")) != NULL)
+ {
+ (void) fgets(name, NODE_LENGTH + 1, file);
+ (void) pclose(file);
+ n = strchr(name, '\n');
+ if (n != NULL)
+ *n = '\0';
+ if (name->nodename[0] != '\0')
+ return (0);
+ }
+#endif
+
+ return (-1);
+}
+#endif /* HASUNAME */
+ /*
+** INITGROUPS -- initialize groups
+**
+** Stub implementation for System V style systems
+*/
+
+#ifndef HASINITGROUPS
+
+initgroups(name, basegid)
+ char *name;
+ int basegid;
+{
+ return 0;
+}
+
+#endif
+ /*
+** SETGROUPS -- set group list
+**
+** Stub implementation for systems that don't have group lists
+*/
+
+#ifndef NGROUPS_MAX
+
+int
+setgroups(ngroups, grouplist)
+ int ngroups;
+ GIDSET_T grouplist[];
+{
+ return 0;
+}
+
+#endif
+ /*
+** SETSID -- set session id (for non-POSIX systems)
+*/
+
+#ifndef HASSETSID
+
+pid_t
+setsid __P ((void))
+{
+#ifdef TIOCNOTTY
+ int fd;
+
+ fd = open("/dev/tty", O_RDWR, 0);
+ if (fd >= 0)
+ {
+ (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
+ (void) close(fd);
+ }
+#endif /* TIOCNOTTY */
+# ifdef SYS5SETPGRP
+ return setpgrp();
+# else
+ return setpgid(0, getpid());
+# endif
+}
+
+#endif
+ /*
+** FSYNC -- dummy fsync
+*/
+
+#ifdef NEEDFSYNC
+
+fsync(fd)
+ int fd;
+{
+# ifdef O_SYNC
+ return fcntl(fd, F_SETFL, O_SYNC);
+# else
+ /* nothing we can do */
+ return 0;
+# endif
+}
+
+#endif
+ /*
+** DGUX_INET_ADDR -- inet_addr for DG/UX
+**
+** Data General DG/UX version of inet_addr returns a struct in_addr
+** instead of a long. This patches things. Only needed on versions
+** prior to 5.4.3.
+*/
+
+#ifdef DGUX_5_4_2
+
+#undef inet_addr
+
+long
+dgux_inet_addr(host)
+ char *host;
+{
+ struct in_addr haddr;
+
+ haddr = inet_addr(host);
+ return haddr.s_addr;
+}
+
+#endif
+ /*
+** GETOPT -- for old systems or systems with bogus implementations
+*/
+
+#ifdef NEEDGETOPT
+
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+
+/*
+** this version hacked to add `atend' flag to allow state machine
+** to reset if invoked by the program to scan args for a 2nd time
+*/
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getopt.c 4.3 (Berkeley) 3/9/86";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+
+/*
+ * get option letter from argument vector
+ */
+#ifdef _CONVEX_SOURCE
+extern int optind, opterr, optopt;
+extern char *optarg;
+#else
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = 0; /* character checked for validity */
+char *optarg = NULL; /* argument associated with option */
+#endif
+
+#define BADCH (int)'?'
+#define EMSG ""
+#define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \
+ fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);}
+
+int
+getopt(nargc,nargv,ostr)
+ int nargc;
+ char *const *nargv;
+ const char *ostr;
+{
+ static char *place = EMSG; /* option letter processing */
+ static char atend = 0;
+ register char *oli = NULL; /* option letter list index */
+
+ if (atend) {
+ atend = 0;
+ place = EMSG;
+ }
+ if(!*place) { /* update scanning pointer */
+ if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) {
+ atend++;
+ return -1;
+ }
+ if (*place == '-') { /* found "--" */
+ ++optind;
+ atend++;
+ return -1;
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) {
+ if (!*place) ++optind;
+ tell(": illegal option -- ");
+ }
+ if (oli && *++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place) ++optind;
+ }
+ else { /* need an argument */
+ if (*place) optarg = place; /* no white space */
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ tell(": option requires an argument -- ");
+ }
+ else optarg = nargv[optind]; /* white space */
+ place = EMSG;
+ ++optind;
+ }
+ return(optopt); /* dump back option letter */
+}
+
+#endif
+ /*
+** VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version
+*/
+
+#ifdef NEEDVPRINTF
+
+#define MAXARG 16
+
+vfprintf(fp, fmt, ap)
+ FILE *fp;
+ char *fmt;
+ char **ap;
+{
+ char *bp[MAXARG];
+ int i = 0;
+
+ while (*ap && i < MAXARG)
+ bp[i++] = *ap++;
+ fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3],
+ bp[4], bp[5], bp[6], bp[7],
+ bp[8], bp[9], bp[10], bp[11],
+ bp[12], bp[13], bp[14], bp[15]);
+}
+
+vsprintf(s, fmt, ap)
+ char *s;
+ char *fmt;
+ char **ap;
+{
+ char *bp[MAXARG];
+ int i = 0;
+
+ while (*ap && i < MAXARG)
+ bp[i++] = *ap++;
+ sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3],
+ bp[4], bp[5], bp[6], bp[7],
+ bp[8], bp[9], bp[10], bp[11],
+ bp[12], bp[13], bp[14], bp[15]);
+}
+
+#endif
+ /*
+** USERSHELLOK -- tell if a user's shell is ok for unrestricted use
+**
+** Parameters:
+** user -- the name of the user we are checking.
+** shell -- the user's shell from /etc/passwd
+**
+** Returns:
+** TRUE -- if it is ok to use this for unrestricted access.
+** FALSE -- if the shell is restricted.
+*/
+
+#if !HASGETUSERSHELL
+
+# ifndef _PATH_SHELLS
+# define _PATH_SHELLS "/etc/shells"
+# endif
+
+# if defined(_AIX3) || defined(_AIX4)
+# include <userconf.h>
+# if _AIX4 >= 40200
+# include <userpw.h>
+# endif
+# include <usersec.h>
+# endif
+
+char *DefaultUserShells[] =
+{
+ "/bin/sh", /* standard shell */
+ "/usr/bin/sh",
+ "/bin/csh", /* C shell */
+ "/usr/bin/csh",
+#ifdef __hpux
+# ifdef V4FS
+ "/usr/bin/rsh", /* restricted Bourne shell */
+ "/usr/bin/ksh", /* Korn shell */
+ "/usr/bin/rksh", /* restricted Korn shell */
+ "/usr/bin/pam",
+ "/usr/bin/keysh", /* key shell (extended Korn shell) */
+ "/usr/bin/posix/sh",
+# else
+ "/bin/rsh", /* restricted Bourne shell */
+ "/bin/ksh", /* Korn shell */
+ "/bin/rksh", /* restricted Korn shell */
+ "/bin/pam",
+ "/usr/bin/keysh", /* key shell (extended Korn shell) */
+ "/bin/posix/sh",
+# endif
+#endif
+#if defined(_AIX3) || defined(_AIX4)
+ "/bin/ksh", /* Korn shell */
+ "/usr/bin/ksh",
+ "/bin/tsh", /* trusted shell */
+ "/usr/bin/tsh",
+ "/bin/bsh", /* Bourne shell */
+ "/usr/bin/bsh",
+#endif
+#ifdef __svr4__
+ "/bin/ksh", /* Korn shell */
+ "/usr/bin/ksh",
+#endif
+#ifdef sgi
+ "/sbin/sh", /* SGI's shells really live in /sbin */
+ "/sbin/csh",
+ "/bin/ksh", /* Korn shell */
+ "/sbin/ksh",
+ "/usr/bin/ksh",
+ "/bin/tcsh", /* Extended csh */
+ "/usr/bin/tcsh",
+#endif
+ NULL
+};
+
+#endif
+
+#define WILDCARD_SHELL "/SENDMAIL/ANY/SHELL/"
+
+bool
+usershellok(user, shell)
+ char *user;
+ char *shell;
+{
+#if HASGETUSERSHELL
+ register char *p;
+ extern char *getusershell();
+
+ if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
+ ConfigLevel <= 1)
+ return TRUE;
+
+ setusershell();
+ while ((p = getusershell()) != NULL)
+ if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0)
+ break;
+ endusershell();
+ return p != NULL;
+#else
+# if USEGETCONFATTR
+ auto char *v;
+# endif
+ register FILE *shellf;
+ char buf[MAXLINE];
+
+ if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
+ ConfigLevel <= 1)
+ return TRUE;
+
+# if USEGETCONFATTR
+ /*
+ ** Naturally IBM has a "better" idea.....
+ **
+ ** What a crock. This interface isn't documented, it is
+ ** considered part of the security library (-ls), and it
+ ** only works if you are running as root (since the list
+ ** of valid shells is obviously a source of great concern).
+ ** I recommend that you do NOT define USEGETCONFATTR,
+ ** especially since you are going to have to set up an
+ ** /etc/shells anyhow to handle the cases where getconfattr
+ ** fails.
+ */
+
+ if (getconfattr(SC_SYS_LOGIN, SC_SHELLS, &v, SEC_LIST) == 0 && v != NULL)
+ {
+ while (*v != '\0')
+ {
+ if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0)
+ return TRUE;
+ v += strlen(v) + 1;
+ }
+ return FALSE;
+ }
+# endif
+
+ shellf = fopen(_PATH_SHELLS, "r");
+ if (shellf == NULL)
+ {
+ /* no /etc/shells; see if it is one of the std shells */
+ char **d;
+
+ if (errno != ENOENT && LogLevel > 3)
+ sm_syslog(LOG_ERR, NOQID,
+ "usershellok: cannot open %s: %s",
+ _PATH_SHELLS, errstring(errno));
+
+ for (d = DefaultUserShells; *d != NULL; d++)
+ {
+ if (strcmp(shell, *d) == 0)
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ while (fgets(buf, sizeof buf, shellf) != NULL)
+ {
+ register char *p, *q;
+
+ p = buf;
+ while (*p != '\0' && *p != '#' && *p != '/')
+ p++;
+ if (*p == '#' || *p == '\0')
+ continue;
+ q = p;
+ while (*p != '\0' && *p != '#' && !(isascii(*p) && isspace(*p)))
+ p++;
+ *p = '\0';
+ if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0)
+ {
+ fclose(shellf);
+ return TRUE;
+ }
+ }
+ fclose(shellf);
+ return FALSE;
+#endif
+}
+ /*
+** FREEDISKSPACE -- see how much free space is on the queue filesystem
+**
+** Only implemented if you have statfs.
+**
+** Parameters:
+** dir -- the directory in question.
+** bsize -- a variable into which the filesystem
+** block size is stored.
+**
+** Returns:
+** The number of bytes free on the queue filesystem.
+** -1 if the statfs call fails.
+**
+** Side effects:
+** Puts the filesystem block size into bsize.
+*/
+
+/* statfs types */
+#define SFS_NONE 0 /* no statfs implementation */
+#define SFS_USTAT 1 /* use ustat */
+#define SFS_4ARGS 2 /* use four-argument statfs call */
+#define SFS_VFS 3 /* use <sys/vfs.h> implementation */
+#define SFS_MOUNT 4 /* use <sys/mount.h> implementation */
+#define SFS_STATFS 5 /* use <sys/statfs.h> implementation */
+#define SFS_STATVFS 6 /* use <sys/statvfs.h> implementation */
+
+#ifndef SFS_TYPE
+# define SFS_TYPE SFS_NONE
+#endif
+
+#if SFS_TYPE == SFS_USTAT
+# include <ustat.h>
+#endif
+#if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
+# include <sys/statfs.h>
+#endif
+#if SFS_TYPE == SFS_VFS
+# include <sys/vfs.h>
+#endif
+#if SFS_TYPE == SFS_MOUNT
+# include <sys/mount.h>
+#endif
+#if SFS_TYPE == SFS_STATVFS
+# include <sys/statvfs.h>
+#endif
+
+long
+freediskspace(dir, bsize)
+ char *dir;
+ long *bsize;
+{
+#if SFS_TYPE != SFS_NONE
+# if SFS_TYPE == SFS_USTAT
+ struct ustat fs;
+ struct stat statbuf;
+# define FSBLOCKSIZE DEV_BSIZE
+# define SFS_BAVAIL f_tfree
+# else
+# if defined(ultrix)
+ struct fs_data fs;
+# define SFS_BAVAIL fd_bfreen
+# define FSBLOCKSIZE 1024L
+# else
+# if SFS_TYPE == SFS_STATVFS
+ struct statvfs fs;
+# define FSBLOCKSIZE fs.f_frsize
+# else
+ struct statfs fs;
+# define FSBLOCKSIZE fs.f_bsize
+# endif
+# endif
+# endif
+# ifndef SFS_BAVAIL
+# define SFS_BAVAIL f_bavail
+# endif
+
+# if SFS_TYPE == SFS_USTAT
+ if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
+# else
+# if SFS_TYPE == SFS_4ARGS
+ if (statfs(dir, &fs, sizeof fs, 0) == 0)
+# else
+# if SFS_TYPE == SFS_STATVFS
+ if (statvfs(dir, &fs) == 0)
+# else
+# if defined(ultrix)
+ if (statfs(dir, &fs) > 0)
+# else
+ if (statfs(dir, &fs) == 0)
+# endif
+# endif
+# endif
+# endif
+ {
+ if (bsize != NULL)
+ *bsize = FSBLOCKSIZE;
+ if (fs.SFS_BAVAIL <= 0)
+ return 0;
+ else if (fs.SFS_BAVAIL > LONG_MAX)
+ return LONG_MAX;
+ else
+ return (long) fs.SFS_BAVAIL;
+ }
+#endif
+ return (-1);
+}
+ /*
+** ENOUGHDISKSPACE -- is there enough free space on the queue fs?
+**
+** Only implemented if you have statfs.
+**
+** Parameters:
+** msize -- the size to check against. If zero, we don't yet
+** know how big the message will be, so just check for
+** a "reasonable" amount.
+**
+** Returns:
+** TRUE if there is enough space.
+** FALSE otherwise.
+*/
+
+bool
+enoughdiskspace(msize)
+ long msize;
+{
+ long bfree, bsize;
+
+ if (MinBlocksFree <= 0 && msize <= 0)
+ {
+ if (tTd(4, 80))
+ printf("enoughdiskspace: no threshold\n");
+ return TRUE;
+ }
+
+ if ((bfree = freediskspace(QueueDir, &bsize)) >= 0)
+ {
+ if (tTd(4, 80))
+ printf("enoughdiskspace: bavail=%ld, need=%ld\n",
+ bfree, msize);
+
+ /* convert msize to block count */
+ msize = msize / bsize + 1;
+ if (MinBlocksFree >= 0)
+ msize += MinBlocksFree;
+
+ if (bfree < msize)
+ {
+ if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, CurEnv->e_id,
+ "low on space (have %ld, %s needs %ld in %s)",
+ bfree,
+ CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
+ msize, QueueDir);
+ return FALSE;
+ }
+ }
+ else if (tTd(4, 80))
+ printf("enoughdiskspace failure: min=%ld, need=%ld: %s\n",
+ MinBlocksFree, msize, errstring(errno));
+ return TRUE;
+}
+ /*
+** TRANSIENTERROR -- tell if an error code indicates a transient failure
+**
+** This looks at an errno value and tells if this is likely to
+** go away if retried later.
+**
+** Parameters:
+** err -- the errno code to classify.
+**
+** Returns:
+** TRUE if this is probably transient.
+** FALSE otherwise.
+*/
+
+bool
+transienterror(err)
+ int err;
+{
+ switch (err)
+ {
+ case EIO: /* I/O error */
+ case ENXIO: /* Device not configured */
+ case EAGAIN: /* Resource temporarily unavailable */
+ case ENOMEM: /* Cannot allocate memory */
+ case ENODEV: /* Operation not supported by device */
+ case ENFILE: /* Too many open files in system */
+ case EMFILE: /* Too many open files */
+ case ENOSPC: /* No space left on device */
+#ifdef ETIMEDOUT
+ case ETIMEDOUT: /* Connection timed out */
+#endif
+#ifdef ESTALE
+ case ESTALE: /* Stale NFS file handle */
+#endif
+#ifdef ENETDOWN
+ case ENETDOWN: /* Network is down */
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH: /* Network is unreachable */
+#endif
+#ifdef ENETRESET
+ case ENETRESET: /* Network dropped connection on reset */
+#endif
+#ifdef ECONNABORTED
+ case ECONNABORTED: /* Software caused connection abort */
+#endif
+#ifdef ECONNRESET
+ case ECONNRESET: /* Connection reset by peer */
+#endif
+#ifdef ENOBUFS
+ case ENOBUFS: /* No buffer space available */
+#endif
+#ifdef ESHUTDOWN
+ case ESHUTDOWN: /* Can't send after socket shutdown */
+#endif
+#ifdef ECONNREFUSED
+ case ECONNREFUSED: /* Connection refused */
+#endif
+#ifdef EHOSTDOWN
+ case EHOSTDOWN: /* Host is down */
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH: /* No route to host */
+#endif
+#ifdef EDQUOT
+ case EDQUOT: /* Disc quota exceeded */
+#endif
+#ifdef EPROCLIM
+ case EPROCLIM: /* Too many processes */
+#endif
+#ifdef EUSERS
+ case EUSERS: /* Too many users */
+#endif
+#ifdef EDEADLK
+ case EDEADLK: /* Resource deadlock avoided */
+#endif
+#ifdef EISCONN
+ case EISCONN: /* Socket already connected */
+#endif
+#ifdef EINPROGRESS
+ case EINPROGRESS: /* Operation now in progress */
+#endif
+#ifdef EALREADY
+ case EALREADY: /* Operation already in progress */
+#endif
+#ifdef EADDRINUSE
+ case EADDRINUSE: /* Address already in use */
+#endif
+#ifdef EADDRNOTAVAIL
+ case EADDRNOTAVAIL: /* Can't assign requested address */
+#endif
+#ifdef ETXTBSY
+ case ETXTBSY: /* (Apollo) file locked */
+#endif
+#if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR))
+ case ENOSR: /* Out of streams resources */
+#endif
+ case E_SM_OPENTIMEOUT: /* PSEUDO: open timed out */
+ return TRUE;
+ }
+
+ /* nope, must be permanent */
+ return FALSE;
+}
+ /*
+** LOCKFILE -- lock a file using flock or (shudder) fcntl locking
+**
+** Parameters:
+** fd -- the file descriptor of the file.
+** filename -- the file name (for error messages).
+** ext -- the filename extension.
+** type -- type of the lock. Bits can be:
+** LOCK_EX -- exclusive lock.
+** LOCK_NB -- non-blocking.
+**
+** Returns:
+** TRUE if the lock was acquired.
+** FALSE otherwise.
+*/
+
+bool
+lockfile(fd, filename, ext, type)
+ int fd;
+ char *filename;
+ char *ext;
+ int type;
+{
+ int i;
+ int save_errno;
+# if !HASFLOCK
+ int action;
+ struct flock lfd;
+
+ if (ext == NULL)
+ ext = "";
+
+ bzero(&lfd, sizeof lfd);
+ if (bitset(LOCK_UN, type))
+ lfd.l_type = F_UNLCK;
+ else if (bitset(LOCK_EX, type))
+ lfd.l_type = F_WRLCK;
+ else
+ lfd.l_type = F_RDLCK;
+
+ if (bitset(LOCK_NB, type))
+ action = F_SETLK;
+ else
+ action = F_SETLKW;
+
+ if (tTd(55, 60))
+ printf("lockfile(%s%s, action=%d, type=%d): ",
+ filename, ext, action, lfd.l_type);
+
+ while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
+ continue;
+ if (i >= 0)
+ {
+ if (tTd(55, 60))
+ printf("SUCCESS\n");
+ return TRUE;
+ }
+ save_errno = errno;
+
+ if (tTd(55, 60))
+ printf("(%s) ", errstring(save_errno));
+
+ /*
+ ** On SunOS, if you are testing using -oQ/tmp/mqueue or
+ ** -oA/tmp/aliases or anything like that, and /tmp is mounted
+ ** as type "tmp" (that is, served from swap space), the
+ ** previous fcntl will fail with "Invalid argument" errors.
+ ** Since this is fairly common during testing, we will assume
+ ** that this indicates that the lock is successfully grabbed.
+ */
+
+ if (save_errno == EINVAL)
+ {
+ if (tTd(55, 60))
+ printf("SUCCESS\n");
+ return TRUE;
+ }
+
+ if (!bitset(LOCK_NB, type) || (save_errno != EACCES && save_errno != EAGAIN))
+ {
+ int omode = -1;
+# ifdef F_GETFL
+ (void) fcntl(fd, F_GETFL, &omode);
+ errno = save_errno;
+# endif
+ syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
+ filename, ext, fd, type, omode, geteuid());
+ dumpfd(fd, TRUE, TRUE);
+ }
+# else
+ if (ext == NULL)
+ ext = "";
+
+ if (tTd(55, 60))
+ printf("lockfile(%s%s, type=%o): ", filename, ext, type);
+
+ while ((i = flock(fd, type)) < 0 && errno == EINTR)
+ continue;
+ if (i >= 0)
+ {
+ if (tTd(55, 60))
+ printf("SUCCESS\n");
+ return TRUE;
+ }
+ save_errno = errno;
+
+ if (tTd(55, 60))
+ printf("(%s) ", errstring(save_errno));
+
+ if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK)
+ {
+ int omode = -1;
+# ifdef F_GETFL
+ (void) fcntl(fd, F_GETFL, &omode);
+ errno = save_errno;
+# endif
+ syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
+ filename, ext, fd, type, omode, geteuid());
+ dumpfd(fd, TRUE, TRUE);
+ }
+# endif
+ if (tTd(55, 60))
+ printf("FAIL\n");
+ errno = save_errno;
+ return FALSE;
+}
+ /*
+** CHOWNSAFE -- tell if chown is "safe" (executable only by root)
+**
+** Unfortunately, given that we can't predict other systems on which
+** a remote mounted (NFS) filesystem will be mounted, the answer is
+** almost always that this is unsafe.
+**
+** Note also that many operating systems have non-compliant
+** implementations of the _POSIX_CHOWN_RESTRICTED variable and the
+** fpathconf() routine. According to IEEE 1003.1-1990, if
+** _POSIX_CHOWN_RESTRICTED is defined and not equal to -1, then
+** no non-root process can give away the file. However, vendors
+** don't take NFS into account, so a comfortable value of
+** _POSIX_CHOWN_RESTRICTED tells us nothing.
+**
+** Also, some systems (e.g., IRIX 6.2) return 1 from fpathconf()
+** even on files where chown is not restricted. Many systems get
+** this wrong on NFS-based filesystems (that is, they say that chown
+** is restricted [safe] on NFS filesystems where it may not be, since
+** other systems can access the same filesystem and do file giveaway;
+** only the NFS server knows for sure!) Hence, it is important to
+** get the value of SAFENFSPATHCONF correct -- it should be defined
+** _only_ after testing (see test/t_pathconf.c) a system on an unsafe
+** NFS-based filesystem to ensure that you can get meaningful results.
+** If in doubt, assume unsafe!
+**
+** You may also need to tweak IS_SAFE_CHOWN -- it should be a
+** condition indicating whether the return from pathconf indicates
+** that chown is safe (typically either > 0 or >= 0 -- there isn't
+** even any agreement about whether a zero return means that a file
+** is or is not safe). It defaults to "> 0".
+**
+** If the parent directory is safe (writable only by owner back
+** to the root) then we can relax slightly and trust fpathconf
+** in more circumstances. This is really a crock -- if this is an
+** NFS mounted filesystem then we really know nothing about the
+** underlying implementation. However, most systems pessimize and
+** return an error (EINVAL or EOPNOTSUPP) on NFS filesystems, which
+** we interpret as unsafe, as we should. Thus, this heuristic gets
+** us into a possible problem only on systems that have a broken
+** pathconf implementation and which are also poorly configured
+** (have :include: files in group- or world-writable directories).
+**
+** Parameters:
+** fd -- the file descriptor to check.
+** safedir -- set if the parent directory is safe.
+**
+** Returns:
+** TRUE -- if the chown(2) operation is "safe" -- that is,
+** only root can chown the file to an arbitrary user.
+** FALSE -- if an arbitrary user can give away a file.
+*/
+
+#ifndef IS_SAFE_CHOWN
+# define IS_SAFE_CHOWN > 0
+#endif
+
+bool
+chownsafe(fd, safedir)
+ int fd;
+ bool safedir;
+{
+#if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
+ (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H))
+ int rval;
+
+ /* give the system administrator a chance to override */
+ if (bitset(DBS_ASSUMESAFECHOWN, DontBlameSendmail))
+ return TRUE;
+
+ /*
+ ** Some systems (e.g., SunOS) seem to have the call and the
+ ** #define _PC_CHOWN_RESTRICTED, but don't actually implement
+ ** the call. This heuristic checks for that.
+ */
+
+ errno = 0;
+ rval = fpathconf(fd, _PC_CHOWN_RESTRICTED);
+# if SAFENFSPATHCONF
+ return errno == 0 && rval IS_SAFE_CHOWN;
+# else
+ return safedir && errno == 0 && rval IS_SAFE_CHOWN;
+# endif
+#else
+ return bitset(DBS_ASSUMESAFECHOWN, DontBlameSendmail);
+#endif
+}
+ /*
+** RESETLIMITS -- reset system controlled resource limits
+**
+** This is to avoid denial-of-service attacks
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+*/
+
+#if HASSETRLIMIT
+# ifdef RLIMIT_NEEDS_SYS_TIME_H
+# include <sys/time.h>
+# endif
+# include <sys/resource.h>
+#endif
+#ifndef FD_SETSIZE
+# define FD_SETSIZE 256
+#endif
+
+void
+resetlimits()
+{
+#if HASSETRLIMIT
+ struct rlimit lim;
+
+ lim.rlim_cur = lim.rlim_max = RLIM_INFINITY;
+ (void) setrlimit(RLIMIT_CPU, &lim);
+ (void) setrlimit(RLIMIT_FSIZE, &lim);
+# ifdef RLIMIT_NOFILE
+ lim.rlim_cur = lim.rlim_max = FD_SETSIZE;
+ (void) setrlimit(RLIMIT_NOFILE, &lim);
+# endif
+#else
+# if HASULIMIT
+ (void) ulimit(2, 0x3fffff);
+ (void) ulimit(4, FD_SETSIZE);
+# endif
+#endif
+ errno = 0;
+}
+ /*
+** GETCFNAME -- return the name of the .cf file.
+**
+** Some systems (e.g., NeXT) determine this dynamically.
+*/
+
+char *
+getcfname()
+{
+
+ if (ConfFile != NULL)
+ return ConfFile;
+#if NETINFO
+ {
+ extern char *ni_propval __P((char *, char *, char *, char *, int));
+ char *cflocation;
+
+ cflocation = ni_propval("/locations", NULL, "sendmail",
+ "sendmail.cf", '\0');
+ if (cflocation != NULL)
+ return cflocation;
+ }
+#endif
+
+ return _PATH_SENDMAILCF;
+}
+ /*
+** SETVENDOR -- process vendor code from V configuration line
+**
+** Parameters:
+** vendor -- string representation of vendor.
+**
+** Returns:
+** TRUE -- if ok.
+** FALSE -- if vendor code could not be processed.
+**
+** Side Effects:
+** It is reasonable to set mode flags here to tweak
+** processing in other parts of the code if necessary.
+** For example, if you are a vendor that uses $%y to
+** indicate YP lookups, you could enable that here.
+*/
+
+bool
+setvendor(vendor)
+ char *vendor;
+{
+ if (strcasecmp(vendor, "Berkeley") == 0)
+ {
+ VendorCode = VENDOR_BERKELEY;
+ return TRUE;
+ }
+
+ /* add vendor extensions here */
+
+#ifdef SUN_EXTENSIONS
+ if (strcasecmp(vendor, "Sun") == 0)
+ {
+ VendorCode = VENDOR_SUN;
+ return TRUE;
+ }
+#endif
+
+ return FALSE;
+}
+ /*
+** VENDOR_PRE_DEFAULTS, VENDOR_POST_DEFAULTS -- set vendor-specific defaults
+**
+** Vendor_pre_defaults is called before reading the configuration
+** file; vendor_post_defaults is called immediately after.
+**
+** Parameters:
+** e -- the global environment to initialize.
+**
+** Returns:
+** none.
+*/
+
+#if SHARE_V1
+int DefShareUid; /* default share uid to run as -- unused??? */
+#endif
+
+void
+vendor_pre_defaults(e)
+ ENVELOPE *e;
+{
+#if SHARE_V1
+ /* OTHERUID is defined in shares.h, do not be alarmed */
+ DefShareUid = OTHERUID;
+#endif
+#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
+ sun_pre_defaults(e);
+#endif
+#ifdef apollo
+ /* stupid domain/os can't even open /etc/sendmail.cf without this */
+ setuserenv("ISP", NULL);
+ setuserenv("SYSTYPE", NULL);
+#endif
+}
+
+
+void
+vendor_post_defaults(e)
+ ENVELOPE *e;
+{
+#ifdef __QNX__
+ char *p;
+
+ /* Makes sure the SOCK environment variable remains */
+ if (p = getextenv("SOCK"))
+ setuserenv("SOCK", p);
+#endif
+#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
+ sun_post_defaults(e);
+#endif
+}
+ /*
+** VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode
+*/
+
+void
+vendor_daemon_setup(e)
+ ENVELOPE *e;
+{
+#if SECUREWARE
+ if (getluid() != -1)
+ {
+ usrerr("Daemon cannot have LUID");
+ exit(EX_USAGE);
+ }
+#endif /* SECUREWARE */
+}
+ /*
+** VENDOR_SET_UID -- do setup for setting a user id
+**
+** This is called when we are still root.
+**
+** Parameters:
+** uid -- the uid we are about to become.
+**
+** Returns:
+** none.
+*/
+
+void
+vendor_set_uid(uid)
+ UID_T uid;
+{
+ /*
+ ** We need to setup the share groups (lnodes)
+ ** and and auditing inforation (luid's)
+ ** before we loose our ``root''ness.
+ */
+#if SHARE_V1
+ if (setupshares(uid, syserr) != 0)
+ syserr("Unable to set up shares");
+#endif
+#if SECUREWARE
+ (void) setup_secure(uid);
+#endif
+}
+ /*
+** VALIDATE_CONNECTION -- check connection for rationality
+**
+** If the connection is rejected, this routine should log an
+** appropriate message -- but should never issue any SMTP protocol.
+**
+** Parameters:
+** sap -- a pointer to a SOCKADDR naming the peer.
+** hostname -- the name corresponding to sap.
+** e -- the current envelope.
+**
+** Returns:
+** error message from rejection.
+** NULL if not rejected.
+*/
+
+#if TCPWRAPPERS
+# include <tcpd.h>
+
+/* tcpwrappers does no logging, but you still have to declare these -- ugh */
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_NOTICE;
+#endif
+
+#if DAEMON
+char *
+validate_connection(sap, hostname, e)
+ SOCKADDR *sap;
+ char *hostname;
+ ENVELOPE *e;
+{
+#if TCPWRAPPERS
+ char *host;
+#endif
+
+ if (tTd(48, 3))
+ printf("validate_connection(%s, %s)\n",
+ hostname, anynet_ntoa(sap));
+
+ if (rscheck("check_relay", hostname, anynet_ntoa(sap), e) != EX_OK)
+ {
+ static char reject[BUFSIZ*2];
+ extern char MsgBuf[];
+
+ if (tTd(48, 4))
+ printf(" ... validate_connection: BAD (rscheck)\n");
+
+ if (strlen(MsgBuf) > 5)
+ {
+ if (isascii(MsgBuf[0]) && isdigit(MsgBuf[0]) &&
+ isascii(MsgBuf[1]) && isdigit(MsgBuf[1]) &&
+ isascii(MsgBuf[2]) && isdigit(MsgBuf[2]))
+ strcpy(reject, &MsgBuf[4]);
+ else
+ strcpy(reject, MsgBuf);
+ }
+ else
+ strcpy(reject, "Access denied");
+
+ return reject;
+ }
+
+#if TCPWRAPPERS
+ if (hostname[0] == '[' && hostname[strlen(hostname) - 1] == ']')
+ host = "unknown";
+ else
+ host = hostname;
+ if (!hosts_ctl("sendmail", host, anynet_ntoa(sap), STRING_UNKNOWN))
+ {
+ if (tTd(48, 4))
+ printf(" ... validate_connection: BAD (tcpwrappers)\n");
+ if (LogLevel >= 4)
+ sm_syslog(LOG_NOTICE, NOQID,
+ "tcpwrappers (%s, %s) rejection",
+ host, anynet_ntoa(sap));
+ return "Access denied";
+ }
+#endif
+ if (tTd(48, 4))
+ printf(" ... validate_connection: OK\n");
+ return NULL;
+}
+
+#endif
+ /*
+** STRTOL -- convert string to long integer
+**
+** For systems that don't have it in the C library.
+**
+** This is taken verbatim from the 4.4-Lite C library.
+*/
+
+#ifdef NEEDSTRTOL
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Convert a string to a long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+
+long
+strtol(nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ register int base;
+{
+ register const char *s = nptr;
+ register unsigned long acc;
+ register int c;
+ register unsigned long cutoff;
+ register int neg = 0, any, cutlim;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
+ cutlim = cutoff % (unsigned long)base;
+ cutoff /= (unsigned long)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
+
+#endif
+ /*
+** STRSTR -- find first substring in string
+**
+** Parameters:
+** big -- the big (full) string.
+** little -- the little (sub) string.
+**
+** Returns:
+** A pointer to the first instance of little in big.
+** big if little is the null string.
+** NULL if little is not contained in big.
+*/
+
+#ifdef NEEDSTRSTR
+
+char *
+strstr(big, little)
+ char *big;
+ char *little;
+{
+ register char *p = big;
+ int l;
+
+ if (*little == '\0')
+ return big;
+ l = strlen(little);
+
+ while ((p = strchr(p, *little)) != NULL)
+ {
+ if (strncmp(p, little, l) == 0)
+ return p;
+ p++;
+ }
+ return NULL;
+}
+
+#endif
+ /*
+** SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
+**
+** Some operating systems have wierd problems with the gethostbyXXX
+** routines. For example, Solaris versions at least through 2.3
+** don't properly deliver a canonical h_name field. This tries to
+** work around these problems.
+*/
+
+struct hostent *
+sm_gethostbyname(name)
+ char *name;
+{
+ struct hostent *h;
+#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4))
+# if SOLARIS == 20300 || SOLARIS == 203
+ static struct hostent hp;
+ static char buf[1000];
+ extern struct hostent *_switch_gethostbyname_r();
+
+ if (tTd(61, 10))
+ printf("_switch_gethostbyname_r(%s)... ", name);
+ h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
+# else
+ extern struct hostent *__switch_gethostbyname();
+
+ if (tTd(61, 10))
+ printf("__switch_gethostbyname(%s)... ", name);
+ h = __switch_gethostbyname(name);
+# endif
+#else
+ int nmaps;
+ char *maptype[MAXMAPSTACK];
+ short mapreturn[MAXMAPACTIONS];
+ char hbuf[MAXNAME];
+
+ if (tTd(61, 10))
+ printf("gethostbyname(%s)... ", name);
+ h = gethostbyname(name);
+ if (h == NULL)
+ {
+ if (tTd(61, 10))
+ printf("failure\n");
+
+ nmaps = switch_map_find("hosts", maptype, mapreturn);
+ while (--nmaps >= 0)
+ if (strcmp(maptype[nmaps], "nis") == 0 ||
+ strcmp(maptype[nmaps], "files") == 0)
+ break;
+ if (nmaps >= 0)
+ {
+ /* try short name */
+ if (strlen(name) > (SIZE_T) sizeof hbuf - 1)
+ return NULL;
+ strcpy(hbuf, name);
+ shorten_hostname(hbuf);
+
+ /* if it hasn't been shortened, there's no point */
+ if (strcmp(hbuf, name) != 0)
+ {
+ if (tTd(61, 10))
+ printf("gethostbyname(%s)... ", hbuf);
+ h = gethostbyname(hbuf);
+ }
+ }
+ }
+#endif
+ if (tTd(61, 10))
+ {
+ if (h == NULL)
+ printf("failure\n");
+ else
+ printf("%s\n", h->h_name);
+ }
+ return h;
+}
+
+struct hostent *
+sm_gethostbyaddr(addr, len, type)
+ char *addr;
+ int len;
+ int type;
+{
+#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204)
+# if SOLARIS == 20300 || SOLARIS == 203
+ static struct hostent hp;
+ static char buf[1000];
+ extern struct hostent *_switch_gethostbyaddr_r();
+
+ return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno);
+# else
+ extern struct hostent *__switch_gethostbyaddr();
+
+ return __switch_gethostbyaddr(addr, len, type);
+# endif
+#else
+ return gethostbyaddr(addr, len, type);
+#endif
+}
+ /*
+** SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid
+*/
+
+struct passwd *
+sm_getpwnam(user)
+ char *user;
+{
+#ifdef _AIX4
+ extern struct passwd *_getpwnam_shadow(const char *, const int);
+
+ return _getpwnam_shadow(user, 0);
+#else
+ return getpwnam(user);
+#endif
+}
+
+struct passwd *
+sm_getpwuid(uid)
+ UID_T uid;
+{
+#if defined(_AIX4) && 0
+ extern struct passwd *_getpwuid_shadow(const int, const int);
+
+ return _getpwuid_shadow(uid,0);
+#else
+ return getpwuid(uid);
+#endif
+}
+ /*
+** SECUREWARE_SETUP_SECURE -- Convex SecureWare setup
+**
+** Set up the trusted computing environment for C2 level security
+** under SecureWare.
+**
+** Parameters:
+** uid -- uid of the user to initialize in the TCB
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Initialized the user in the trusted computing base
+*/
+
+#if SECUREWARE
+
+# include <sys/security.h>
+# include <prot.h>
+
+void
+secureware_setup_secure(uid)
+ UID_T uid;
+{
+ int rc;
+
+ if (getluid() != -1)
+ return;
+
+ if ((rc = set_secure_info(uid)) != SSI_GOOD_RETURN)
+ {
+ switch (rc)
+ {
+ case SSI_NO_PRPW_ENTRY:
+ syserr("No protected passwd entry, uid = %d", uid);
+ break;
+
+ case SSI_LOCKED:
+ syserr("Account has been disabled, uid = %d", uid);
+ break;
+
+ case SSI_RETIRED:
+ syserr("Account has been retired, uid = %d", uid);
+ break;
+
+ case SSI_BAD_SET_LUID:
+ syserr("Could not set LUID, uid = %d", uid);
+ break;
+
+ case SSI_BAD_SET_PRIVS:
+ syserr("Could not set kernel privs, uid = %d", uid);
+
+ default:
+ syserr("Unknown return code (%d) from set_secure_info(%d)",
+ rc, uid);
+ break;
+ }
+ exit(EX_NOPERM);
+ }
+}
+#endif /* SECUREWARE */
+ /*
+** LOAD_IF_NAMES -- load interface-specific names into $=w
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Loads $=w with the names of all the interfaces.
+*/
+
+#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
+struct rtentry;
+struct mbuf;
+# include <arpa/inet.h>
+# ifndef SUNOS403
+# include <sys/time.h>
+# endif
+# if _AIX4 >= 40300
+# undef __P
+# endif
+# include <net/if.h>
+#endif
+
+void
+load_if_names()
+{
+#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
+ int s;
+ int i;
+ struct ifconf ifc;
+ int numifs;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1)
+ return;
+
+ /* get the list of known IP address from the kernel */
+# if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN
+ if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0)
+ {
+ /* can't get number of interfaces -- fall back */
+ if (tTd(0, 4))
+ printf("SIOCGIFNUM failed: %s\n", errstring(errno));
+ numifs = -1;
+ }
+ else if (tTd(0, 42))
+ printf("system has %d interfaces\n", numifs);
+ if (numifs < 0)
+# endif
+ numifs = 512;
+
+ if (numifs <= 0)
+ {
+ close(s);
+ return;
+ }
+ ifc.ifc_len = numifs * sizeof (struct ifreq);
+ ifc.ifc_buf = xalloc(ifc.ifc_len);
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
+ {
+ if (tTd(0, 4))
+ printf("SIOGIFCONF failed: %s\n", errstring(errno));
+ close(s);
+ return;
+ }
+
+ /* scan the list of IP address */
+ if (tTd(0, 40))
+ printf("scanning for interface specific names, ifc_len=%d\n",
+ ifc.ifc_len);
+
+ for (i = 0; i < ifc.ifc_len; )
+ {
+ struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i];
+ struct sockaddr *sa = &ifr->ifr_addr;
+ struct in_addr ia;
+ struct hostent *hp;
+#ifdef SIOCGIFFLAGS
+ struct ifreq ifrf;
+#endif
+ char ip_addr[256];
+ extern char *inet_ntoa();
+
+#ifdef BSD4_4_SOCKADDR
+ if (sa->sa_len > sizeof ifr->ifr_addr)
+ i += sizeof ifr->ifr_name + sa->sa_len;
+ else
+#endif
+ i += sizeof *ifr;
+
+ if (tTd(0, 20))
+ printf("%s\n", anynet_ntoa((SOCKADDR *) sa));
+
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+
+#ifdef SIOCGIFFLAGS
+ bzero(&ifrf, sizeof(struct ifreq));
+ strncpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name));
+ ioctl(s, SIOCGIFFLAGS, (char *) &ifrf);
+ if (tTd(0, 41))
+ printf("\tflags: %x\n", ifrf.ifr_flags);
+# define IFRFREF ifrf
+#else
+# define IFRFREF (*ifr)
+#endif
+ if (!bitset(IFF_UP, IFRFREF.ifr_flags))
+ continue;
+
+ /* extract IP address from the list*/
+ ia = (((struct sockaddr_in *) sa)->sin_addr);
+ if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE)
+ {
+ message("WARNING: interface %s is UP with %s address",
+ ifr->ifr_name, inet_ntoa(ia));
+ continue;
+ }
+
+ /* save IP address in text from */
+ (void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]",
+ sizeof ip_addr - 3,
+ inet_ntoa(ia));
+ if (!wordinclass(ip_addr, 'w'))
+ {
+ setclass('w', ip_addr);
+ if (tTd(0, 4))
+ printf("\ta.k.a.: %s\n", ip_addr);
+ }
+
+ /* skip "loopback" interface "lo" */
+ if (bitset(IFF_LOOPBACK, IFRFREF.ifr_flags))
+ continue;
+
+ /* lookup name with IP address */
+ hp = sm_gethostbyaddr((char *) &ia, sizeof(ia), AF_INET);
+ if (hp == NULL)
+ {
+ if (LogLevel > 3)
+ sm_syslog(LOG_WARNING, NOQID,
+ "gethostbyaddr(%.100s) failed: %d\n",
+ inet_ntoa(ia),
+#if NAMED_BIND
+ h_errno);
+#else
+ -1);
+#endif
+ continue;
+ }
+
+ /* save its cname */
+ if (!wordinclass((char *) hp->h_name, 'w'))
+ {
+ setclass('w', (char *) hp->h_name);
+ if (tTd(0, 4))
+ printf("\ta.k.a.: %s\n", hp->h_name);
+ }
+
+ /* save all it aliases name */
+ while (*hp->h_aliases)
+ {
+ if (!wordinclass(*hp->h_aliases, 'w'))
+ {
+ setclass('w', *hp->h_aliases);
+ if (tTd(0, 4))
+ printf("\ta.k.a.: %s\n", *hp->h_aliases);
+ }
+ hp->h_aliases++;
+ }
+ }
+ free(ifc.ifc_buf);
+ close(s);
+# undef IFRFREF
+#endif
+}
+ /*
+** GET_NUM_PROCS_ONLINE -- return the number of processors currently online
+**
+** Parameters:
+** none.
+**
+** Returns:
+** The number of processors online.
+*/
+
+int
+get_num_procs_online()
+{
+ int nproc = 0;
+
+#if _FFR_SCALE_LA_BY_NUM_PROCS
+#ifdef _SC_NPROCESSORS_ONLN
+ nproc = (int) sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+#endif
+ if (nproc <= 0)
+ nproc = 1;
+ return nproc;
+}
+ /*
+** SM_SYSLOG -- syslog wrapper to keep messages under SYSLOG_BUFSIZE
+**
+** Parameters:
+** level -- syslog level
+** id -- envelope ID or NULL (NOQUEUE)
+** fmt -- format string
+** arg... -- arguments as implied by fmt.
+**
+** Returns:
+** none
+*/
+
+/* VARARGS3 */
+void
+# ifdef __STDC__
+sm_syslog(int level, const char *id, const char *fmt, ...)
+# else
+sm_syslog(level, id, fmt, va_alist)
+ int level;
+ const char *id;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ static char *buf = NULL;
+ static size_t bufsize = MAXLINE;
+ char *begin, *end;
+ int seq = 1;
+ int idlen;
+ extern int SnprfOverflow;
+ extern int SyslogErrno;
+ extern char *DoprEnd;
+ VA_LOCAL_DECL
+ extern void sm_dopr __P((char *, const char *, ...));
+
+ SyslogErrno = errno;
+ if (id == NULL)
+ {
+ id = "NOQUEUE";
+ idlen = 9;
+ }
+ else if (strcmp(id, NOQID) == 0)
+ {
+ id = "";
+ idlen = 0;
+ }
+ else
+ idlen = strlen(id + 2);
+bufalloc:
+ if (buf == NULL)
+ buf = (char *) xalloc(sizeof(char) * bufsize);
+
+ /* do a virtual vsnprintf into buf */
+ VA_START(fmt);
+ buf[0] = 0;
+ DoprEnd = buf + bufsize - 1;
+ SnprfOverflow = 0;
+ sm_dopr(buf, fmt, ap);
+ *DoprEnd = '\0';
+ VA_END;
+ /* end of virtual vsnprintf */
+
+ if (SnprfOverflow)
+ {
+ /* String too small, redo with correct size */
+ bufsize += SnprfOverflow + 1;
+ free(buf);
+ buf = NULL;
+ goto bufalloc;
+ }
+ if ((strlen(buf) + idlen + 1) < SYSLOG_BUFSIZE)
+ {
+#if LOG
+ if (*id == '\0')
+ syslog(level, "%s", buf);
+ else
+ syslog(level, "%s: %s", id, buf);
+#else
+ /*XXX should do something more sensible */
+ if (*id == '\0')
+ fprintf(stderr, "%s\n", buf);
+ else
+ fprintf(stderr, "%s: %s\n", id, buf);
+#endif
+ return;
+ }
+
+ begin = buf;
+ while (*begin != '\0' &&
+ (strlen(begin) + idlen + 5) > SYSLOG_BUFSIZE)
+ {
+ char save;
+
+ if (seq == 999)
+ {
+ /* Too many messages */
+ break;
+ }
+ end = begin + SYSLOG_BUFSIZE - idlen - 12;
+ while (end > begin)
+ {
+ /* Break on comma or space */
+ if (*end == ',' || *end == ' ')
+ {
+ end++; /* Include separator */
+ break;
+ }
+ end--;
+ }
+ /* No separator, break midstring... */
+ if (end == begin)
+ end = begin + SYSLOG_BUFSIZE - idlen - 12;
+ save = *end;
+ *end = 0;
+#if LOG
+ syslog(level, "%s[%d]: %s ...", id, seq++, begin);
+#else
+ fprintf(stderr, "%s[%d]: %s ...\n", id, seq++, begin);
+#endif
+ *end = save;
+ begin = end;
+ }
+ if (seq == 999)
+#if LOG
+ syslog(level, "%s[%d]: log terminated, too many parts", id, seq);
+#else
+ fprintf(stderr, "%s[%d]: log terminated, too many parts\n", id, seq);
+#endif
+ else if (*begin != '\0')
+#if LOG
+ syslog(level, "%s[%d]: %s", id, seq, begin);
+#else
+ fprintf(stderr, "%s[%d]: %s\n", id, seq, begin);
+#endif
+}
+ /*
+** HARD_SYSLOG -- call syslog repeatedly until it works
+**
+** Needed on HP-UX, which apparently doesn't guarantee that
+** syslog succeeds during interrupt handlers.
+*/
+
+#if defined(__hpux) && !defined(HPUX11)
+
+# define MAXSYSLOGTRIES 100
+# undef syslog
+# ifdef V4FS
+# define XCNST const
+# define CAST (const char *)
+# else
+# define XCNST
+# define CAST
+# endif
+
+void
+# ifdef __STDC__
+hard_syslog(int pri, XCNST char *msg, ...)
+# else
+hard_syslog(pri, msg, va_alist)
+ int pri;
+ XCNST char *msg;
+ va_dcl
+# endif
+{
+ int i;
+ char buf[SYSLOG_BUFSIZE];
+ VA_LOCAL_DECL;
+
+ VA_START(msg);
+ vsnprintf(buf, sizeof buf, msg, ap);
+ VA_END;
+
+ for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; )
+ continue;
+}
+
+# undef CAST
+#endif
+ /*
+** LOCAL_HOSTNAME_LENGTH
+**
+** This is required to get sendmail to compile against BIND 4.9.x
+** on Ultrix.
+*/
+
+#if defined(ultrix) && NAMED_BIND
+
+# include <resolv.h>
+# if __RES >= 19931104 && __RES < 19950621
+
+int
+local_hostname_length(hostname)
+ char *hostname;
+{
+ int len_host, len_domain;
+
+ if (!*_res.defdname)
+ res_init();
+ len_host = strlen(hostname);
+ len_domain = strlen(_res.defdname);
+ if (len_host > len_domain &&
+ (strcasecmp(hostname + len_host - len_domain,_res.defdname) == 0) &&
+ hostname[len_host - len_domain - 1] == '.')
+ return len_host - len_domain - 1;
+ else
+ return 0;
+}
+
+# endif
+#endif
+ /*
+** Compile-Time options
+*/
+
+char *CompileOptions[] =
+{
+#ifdef HESIOD
+ "HESIOD",
+#endif
+#if HES_GETMAILHOST
+ "HES_GETMAILHOST",
+#endif
+#ifdef LDAPMAP
+ "LDAPMAP",
+#endif
+#ifdef MAP_REGEX
+ "MAP_REGEX",
+#endif
+#if LOG
+ "LOG",
+#endif
+#if MATCHGECOS
+ "MATCHGECOS",
+#endif
+#if MIME7TO8
+ "MIME7TO8",
+#endif
+#if MIME8TO7
+ "MIME8TO7",
+#endif
+#if NAMED_BIND
+ "NAMED_BIND",
+#endif
+#ifdef NDBM
+ "NDBM",
+#endif
+#if NETINET
+ "NETINET",
+#endif
+#if NETINFO
+ "NETINFO",
+#endif
+#if NETISO
+ "NETISO",
+#endif
+#if NETNS
+ "NETNS",
+#endif
+#if NETUNIX
+ "NETUNIX",
+#endif
+#if NETX25
+ "NETX25",
+#endif
+#ifdef NEWDB
+ "NEWDB",
+#endif
+#ifdef NIS
+ "NIS",
+#endif
+#ifdef NISPLUS
+ "NISPLUS",
+#endif
+#if QUEUE
+ "QUEUE",
+#endif
+#if SCANF
+ "SCANF",
+#endif
+#if SMTP
+ "SMTP",
+#endif
+#if SMTPDEBUG
+ "SMTPDEBUG",
+#endif
+#ifdef SUID_ROOT_FILES_OK
+ "SUID_ROOT_FILES_OK",
+#endif
+#if TCPWRAPPERS
+ "TCPWRAPPERS",
+#endif
+#if USERDB
+ "USERDB",
+#endif
+#if XDEBUG
+ "XDEBUG",
+#endif
+#ifdef XLA
+ "XLA",
+#endif
+ NULL
+};
+
+
+/*
+** OS compile options.
+*/
+
+char *OsCompileOptions[] =
+{
+#if BOGUS_O_EXCL
+ "BOGUS_O_EXCL",
+#endif
+#if HASFCHMOD
+ "HASFCHMOD",
+#endif
+#if HASFLOCK
+ "HASFLOCK",
+#endif
+#if HASGETDTABLESIZE
+ "HASGETDTABLESIZE",
+#endif
+#if HASGETUSERSHELL
+ "HASGETUSERSHELL",
+#endif
+#if HASINITGROUPS
+ "HASINITGROUPS",
+#endif
+#if HASLSTAT
+ "HASLSTAT",
+#endif
+#if HASSETREUID
+ "HASSETREUID",
+#endif
+#if HASSETRLIMIT
+ "HASSETRLIMIT",
+#endif
+#if HASSETSID
+ "HASSETSID",
+#endif
+#if HASSETUSERCONTEXT
+ "HASSETUSERCONTEXT",
+#endif
+#if HASSETVBUF
+ "HASSETVBUF",
+#endif
+#if HASSNPRINTF
+ "HASSNPRINTF",
+#endif
+#if HAS_ST_GEN
+ "HAS_ST_GEN",
+#endif
+#if HASSTRERROR
+ "HASSTRERROR",
+#endif
+#if HASULIMIT
+ "HASULIMIT",
+#endif
+#if HASUNAME
+ "HASUNAME",
+#endif
+#if HASUNSETENV
+ "HASUNSETENV",
+#endif
+#if HASWAITPID
+ "HASWAITPID",
+#endif
+#if IDENTPROTO
+ "IDENTPROTO",
+#endif
+#if IP_SRCROUTE
+ "IP_SRCROUTE",
+#endif
+#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
+ "LOCK_ON_OPEN",
+#endif
+#if NEEDFSYNC
+ "NEEDFSYNC",
+#endif
+#if NOFTRUNCATE
+ "NOFTRUNCATE",
+#endif
+#if RLIMIT_NEEDS_SYS_TIME_H
+ "RLIMIT_NEEDS_SYS_TIME_H",
+#endif
+#if SAFENFSPATHCONF
+ "SAFENFSPATHCONF",
+#endif
+#if SECUREWARE
+ "SECUREWARE",
+#endif
+#if SHARE_V1
+ "SHARE_V1",
+#endif
+#if SIOCGIFCONF_IS_BROKEN
+ "SIOCGIFCONF_IS_BROKEN",
+#endif
+#if SIOCGIFNUM_IS_BROKEN
+ "SIOCGIFNUM_IS_BROKEN",
+#endif
+#if SYS5SETPGRP
+ "SYS5SETPGRP",
+#endif
+#if SYSTEM5
+ "SYSTEM5",
+#endif
+#if USE_SA_SIGACTION
+ "USE_SA_SIGACTION",
+#endif
+#if USE_SIGLONGJMP
+ "USE_SIGLONGJMP",
+#endif
+#if USESETEUID
+ "USESETEUID",
+#endif
+ NULL
+};
diff --git a/contrib/sendmail/src/conf.h b/contrib/sendmail/src/conf.h
new file mode 100644
index 0000000..fd14d5a
--- /dev/null
+++ b/contrib/sendmail/src/conf.h
@@ -0,0 +1,2440 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ *
+ * @(#)conf.h 8.372 (Berkeley) 6/4/98
+ */
+
+/*
+** CONF.H -- All user-configurable parameters for sendmail
+**
+** Send updates to sendmail@Sendmail.ORG so they will be
+** included in the next release.
+*/
+
+#ifdef __GNUC__
+struct rusage; /* forward declaration to get gcc to shut up in wait.h */
+#endif
+
+# include <sys/param.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+#ifndef __QNX__
+/* in QNX this grabs bogus LOCK_* manifests */
+# include <sys/file.h>
+#endif
+# include <sys/wait.h>
+# include <limits.h>
+# include <fcntl.h>
+# include <signal.h>
+# include <netdb.h>
+# include <pwd.h>
+
+/**********************************************************************
+** Table sizes, etc....
+** There shouldn't be much need to change these....
+**********************************************************************/
+
+# define MAXLINE 2048 /* max line length */
+# define MAXNAME 256 /* max length of a name */
+# define MAXPV 40 /* max # of parms to mailers */
+# define MAXATOM 200 /* max atoms per address */
+# define MAXMAILERS 25 /* maximum mailers known to system */
+# define MAXRWSETS 200 /* max # of sets of rewriting rules */
+# define MAXPRIORITIES 25 /* max values for Precedence: field */
+# define MAXMXHOSTS 100 /* max # of MX records for one host */
+# define SMTPLINELIM 990 /* maximum SMTP line length */
+# define MAXKEY 128 /* maximum size of a database key */
+# define MEMCHUNKSIZE 1024 /* chunk size for memory allocation */
+# define MAXUSERENVIRON 100 /* max envars saved, must be >= 3 */
+# define MAXALIASDB 12 /* max # of alias databases */
+# define MAXMAPSTACK 12 /* max # of stacked or sequenced maps */
+# define MAXTOCLASS 8 /* max # of message timeout classes */
+# define MAXMIMEARGS 20 /* max args in Content-Type: */
+# define MAXMIMENESTING 20 /* max MIME multipart nesting */
+# define QUEUESEGSIZE 1000 /* increment for queue size */
+# define MAXQFNAME 20 /* max qf file name length */
+# define MACBUFSIZE 4096 /* max expanded macro buffer size */
+# define TOBUFSIZE 512 /* max buffer to hold address list */
+# define MAXSHORTSTR 203 /* max short string length */
+
+/**********************************************************************
+** Compilation options.
+** #define these to 1 if they are available;
+** #define them to 0 otherwise.
+** All can be overridden from Makefile.
+**********************************************************************/
+
+# ifndef NETINET
+# define NETINET 1 /* include internet support */
+# endif
+
+# ifndef NETISO
+# define NETISO 0 /* do not include ISO socket support */
+# endif
+
+# ifndef NAMED_BIND
+# define NAMED_BIND 1 /* use Berkeley Internet Domain Server */
+# endif
+
+# ifndef XDEBUG
+# define XDEBUG 1 /* enable extended debugging */
+# endif
+
+# ifndef MATCHGECOS
+# define MATCHGECOS 1 /* match user names from gecos field */
+# endif
+
+# ifndef DSN
+# define DSN 1 /* include delivery status notification code */
+# endif
+
+# if !defined(USERDB) && (defined(NEWDB) || defined(HESIOD))
+# define USERDB 1 /* look in user database */
+# endif
+
+# ifndef MIME8TO7
+# define MIME8TO7 1 /* 8->7 bit MIME conversions */
+# endif
+
+# ifndef MIME7TO8
+# define MIME7TO8 1 /* 7->8 bit MIME conversions */
+# endif
+
+/**********************************************************************
+** "Hard" compilation options.
+** #define these if they are available; comment them out otherwise.
+** These cannot be overridden from the Makefile, and should really not
+** be turned off unless absolutely necessary.
+**********************************************************************/
+
+# define LOG 1 /* enable logging -- don't turn off */
+
+/**********************************************************************
+** End of site-specific configuration.
+**********************************************************************/
+ /*
+** General "standard C" defines.
+**
+** These may be undone later, to cope with systems that claim to
+** be Standard C but aren't. Gcc is the biggest offender -- it
+** doesn't realize that the library is part of the language.
+**
+** Life would be much easier if we could get rid of this sort
+** of bozo problems.
+*/
+
+#ifdef __STDC__
+# define HASSETVBUF 1 /* we have setvbuf(3) in libc */
+#endif
+
+/*
+** Assume you have standard calls; can be #undefed below if necessary.
+*/
+
+# define HASLSTAT 1 /* has lstat(2) call */
+ /**********************************************************************
+** Operating system configuration.
+**
+** Unless you are porting to a new OS, you shouldn't have to
+** change these.
+**********************************************************************/
+
+/*
+** HP-UX -- tested for 8.07, 9.00, and 9.01.
+**
+** If V4FS is defined, compile for HP-UX 10.0.
+** 11.x support from Richard Allen <ra@hp.is>.
+*/
+
+#ifdef __hpux
+ /* common definitions for HP-UX 9.x and 10.x */
+# undef m_flags /* conflict between Berkeley DB 1.85 db.h & sys/sysmacros.h on HP 300 */
+# define SYSTEM5 1 /* include all the System V defines */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define USESETEUID 1 /* has useable seteuid(2) call */
+# define BOGUS_O_EXCL 1 /* exclusive open follows symlinks */
+# define seteuid(e) setresuid(-1, e, -1)
+# define IP_SRCROUTE 1 /* can check IP source routing */
+# define LA_TYPE LA_HPUX
+# define SPT_TYPE SPT_PSTAT
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# define GIDSET_T gid_t
+# ifndef HASGETUSERSHELL
+# define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */
+# endif
+# ifndef HPUX11
+# define syslog hard_syslog
+# endif
+# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */
+
+# ifdef V4FS
+ /* HP-UX 10.x */
+# define _PATH_UNIX "/stand/vmunix"
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid"
+# endif
+# ifndef IDENTPROTO
+# define IDENTPROTO 1 /* TCP/IP implementation fixed in 10.0 */
+# endif
+
+# else
+ /* HP-UX 9.x */
+# define _PATH_UNIX "/hp-ux"
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+# ifdef __STDC__
+extern void hard_syslog(int, char *, ...);
+# else
+extern void hard_syslog();
+# endif
+# define FDSET_CAST (int *) /* cast for fd_set parameters to select */
+# endif
+
+#endif
+
+
+/*
+** IBM AIX 4.x
+*/
+
+#ifdef _AIX4
+# define _AIX3 1 /* pull in AIX3 stuff */
+# define USESETEUID 1 /* seteuid(2) works */
+# define TZ_TYPE TZ_NAME /* use tzname[] vector */
+# define SOCKOPT_LEN_T size_t /* arg#5 to getsockopt */
+# if _AIX4 >= 40200
+# define HASSETREUID 1 /* setreuid(2) works as of AIX 4.2 */
+# define SOCKADDR_LEN_T size_t /* e.g., arg#3 to accept, getsockname */
+# endif
+# if defined(_ILS_MACROS) /* IBM versions aren't side-effect clean */
+# undef isascii
+# define isascii(c) !(c & ~0177)
+# undef isdigit
+# define isdigit(__a) (_IS(__a,_ISDIGIT))
+# undef isspace
+# define isspace(__a) (_IS(__a,_ISSPACE))
+# endif
+#endif
+
+
+/*
+** IBM AIX 3.x -- actually tested for 3.2.3
+*/
+
+#ifdef _AIX3
+# include <paths.h>
+# include <sys/machine.h> /* to get byte order */
+# include <sys/select.h>
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */
+# define GIDSET_T gid_t
+# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+# define LA_TYPE LA_INT
+# define FSHIFT 16
+# define LA_AVENRUN "avenrun"
+#endif
+
+
+/*
+** IBM AIX 2.2.1 -- actually tested for osupdate level 2706+1773
+**
+** From Mark Whetzel <markw@wg.waii.com>.
+*/
+
+#ifdef AIX /* AIX/RT compiler pre-defines this */
+# include <paths.h>
+# include <sys/time.h> /* AIX/RT resource.h does NOT include this */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# define HASFCHMOD 0 /* does not have fchmod(2) syscall */
+# define HASSETREUID 1 /* use setreuid(2) -lbsd system call */
+# define HASSETVBUF 1 /* use setvbuf(2) system call */
+# define HASSETRLIMIT 0 /* does not have setrlimit call */
+# define HASFLOCK 0 /* does not have flock call - use fcntl */
+# define HASULIMIT 1 /* use ulimit instead of setrlimit call */
+# define NEEDGETOPT 1 /* Do we need theirs or ours */
+# define SYS5SETPGRP 1 /* don't have setpgid on AIX/RT */
+# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */
+# define BSD4_3 1 /* NOT bsd 4.4 or posix signals */
+# define GIDSET_T int
+# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+# define LA_TYPE LA_SUBR /* use our ported loadavgd daemon */
+# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */
+# define ARBPTR_T int *
+# define void int
+typedef int pid_t;
+/* RTisms for BSD compatibility, specified in the Makefile
+ define BSD 1
+ define BSD_INCLUDES 1
+ define BSD_REMAP_SIGNAL_TO_SIGVEC
+ RTisms needed above */
+/* make this sendmail in a completely different place */
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/local/newmail/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/usr/local/newmail/sendmail.pid"
+# endif
+#endif
+
+
+/*
+** Silicon Graphics IRIX
+**
+** Compiles on 4.0.1.
+**
+** Use IRIX64 instead of IRIX for 64-bit IRIX (6.0).
+** Use IRIX5 instead of IRIX for IRIX 5.x.
+**
+** This version tries to be adaptive using _MIPS_SIM:
+** _MIPS_SIM == _ABIO32 (= 1) Abi: -32 on IRIX 6.2
+** _MIPS_SIM == _ABIN32 (= 2) Abi: -n32 on IRIX 6.2
+** _MIPS_SIM == _ABI64 (= 3) Abi: -64 on IRIX 6.2
+**
+** _MIPS_SIM is 1 also on IRIX 5.3
+**
+** IRIX64 changes from Mark R. Levinson <ml@cvdev.rochester.edu>.
+** IRIX5 changes from Kari E. Hurtta <Kari.Hurtta@fmi.fi>.
+** Adaptive changes from Kari E. Hurtta <Kari.Hurtta@fmi.fi>.
+*/
+
+#if defined(__sgi)
+# ifndef IRIX
+# define IRIX
+# endif
+# if _MIPS_SIM > 0 && !defined(IRIX5)
+# define IRIX5 /* IRIX5 or IRIX6 */
+# endif
+# if _MIPS_SIM > 1 && !defined(IRIX6) && !defined(IRIX64)
+# define IRIX6 /* IRIX6 */
+# endif
+
+#endif
+
+#ifdef IRIX
+# define SYSTEM5 1 /* this is a System-V derived system */
+# define HASSETREUID 1 /* has setreuid(2) call */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# define IP_SRCROUTE 1 /* can check IP source routing */
+# define setpgid BSDsetpgrp
+# define GIDSET_T gid_t
+# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */
+# define SFS_BAVAIL f_bfree /* alternate field name */
+# ifdef IRIX6
+# define STAT64 1
+# define QUAD_T unsigned long long
+# define LA_TYPE LA_IRIX6 /* figure out at run time */
+# define SAFENFSPATHCONF 0 /* pathconf(2) lies on NFS filesystems */
+# define SYSLOG_BUFSIZE 512
+# else
+# define LA_TYPE LA_INT
+
+# ifdef IRIX64
+# define STAT64 1
+# define QUAD_T unsigned long long
+# define NAMELISTMASK 0x7fffffffffffffff /* mask for nlist() values */
+# else
+# define STAT64 0
+# define NAMELISTMASK 0x7fffffff /* mask for nlist() values */
+# endif
+# endif
+# if defined(IRIX64) || defined(IRIX5) || defined(IRIX6)
+# include <sys/cdefs.h>
+# include <paths.h>
+# define ARGV_T char *const *
+# define HASSETRLIMIT 1 /* has setrlimit(2) syscall */
+# define HASGETDTABLESIZE 1 /* has getdtablesize(2) syscall */
+# define HASSTRERROR 1 /* has strerror(3) */
+# else
+# define ARGV_T const char **
+# define WAITUNION 1 /* use "union wait" as wait argument type */
+# endif
+#endif
+
+
+/*
+** SunOS and Solaris
+**
+** Tested on SunOS 4.1.x (a.k.a. Solaris 1.1.x) and
+** Solaris 2.4 (a.k.a. SunOS 5.4).
+*/
+
+#if defined(sun) && !defined(BSD)
+
+# include <sys/time.h>
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define IP_SRCROUTE 1 /* can check IP source routing */
+# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */
+
+# ifdef SOLARIS_2_3
+# define SOLARIS 20300 /* for back compat only -- use -DSOLARIS=20300 */
+# endif
+
+# if defined(NOT_SENDMAIL) && !defined(SOLARIS) && defined(sun) && (defined(__svr4__) || defined(__SVR4))
+# define SOLARIS 1 /* unknown Solaris version */
+# endif
+
+# ifdef SOLARIS
+ /* Solaris 2.x (a.k.a. SunOS 5.x) */
+# ifndef __svr4__
+# define __svr4__ /* use all System V Releae 4 defines below */
+# endif
+# define GIDSET_T gid_t
+# define USE_SA_SIGACTION 1 /* use sa_sigaction field */
+# ifndef _PATH_UNIX
+# define _PATH_UNIX "/dev/ksyms"
+# endif
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid"
+# endif
+# ifndef _PATH_HOSTS
+# define _PATH_HOSTS "/etc/inet/hosts"
+# endif
+# ifndef SYSLOG_BUFSIZE
+# define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */
+# endif
+# ifndef TZ_TYPE
+# define TZ_TYPE TZ_TZNAME
+# endif
+# if SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203)
+# define USESETEUID 1 /* seteuid works as of 2.3 */
+# endif
+# if SOLARIS >= 20500 || (SOLARIS < 10000 && SOLARIS >= 205)
+# define HASSETREUID 1 /* setreuid works as of 2.5 */
+# if SOLARIS < 207 || (SOLARIS > 10000 && SOLARIS < 20700)
+# ifndef LA_TYPE
+# define LA_TYPE LA_KSTAT /* use kstat(3k) -- may work in < 2.5 */
+# endif
+# endif
+# endif
+# if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206)
+# define HASSNPRINTF 1 /* has snprintf starting in 2.6 */
+# endif
+# if SOLARIS >= 20700 || (SOLARIS < 10000 && SOLARIS >= 207)
+# ifndef LA_TYPE
+# define LA_TYPE LA_SUBR /* getloadavg(3c) appears in 2.7 */
+# endif
+# define HASGETUSERSHELL 1 /* getusershell(3c) bug fixed in 2.7 */
+# endif
+# ifndef HASGETUSERSHELL
+# define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps pre-2.7 */
+# endif
+
+# else
+ /* SunOS 4.0.3 or 4.1.x */
+# define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */
+# define HASSETREUID 1 /* has setreuid(2) call */
+# ifndef HASFLOCK
+# define HASFLOCK 1 /* has flock(2) call */
+# endif
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */
+# include <memory.h>
+# include <vfork.h>
+# ifdef __GNUC__
+# define strtoul strtol /* gcc library bogosity */
+# endif
+
+# ifdef SUNOS403
+ /* special tweaking for SunOS 4.0.3 */
+# include <malloc.h>
+# define BSD4_3 1 /* 4.3 BSD-based */
+# define NEEDSTRSTR 1 /* need emulation of strstr(3) routine */
+# define WAITUNION 1 /* use "union wait" as wait argument type */
+# undef WIFEXITED
+# undef WEXITSTATUS
+# undef HASUNAME
+# define setpgid setpgrp
+# define MODE_T int
+typedef int pid_t;
+extern char *getenv();
+
+# else
+ /* 4.1.x specifics */
+# define HASSETSID 1 /* has Posix setsid(2) call */
+# define HASSETVBUF 1 /* we have setvbuf(3) in libc */
+
+# endif
+# endif
+
+# ifndef LA_TYPE
+# define LA_TYPE LA_INT
+# endif
+
+#endif /* sun && !BSD */
+
+/*
+** DG/UX
+**
+** Tested on 5.4.2 and 5.4.3. Use DGUX_5_4_2 to get the
+** older support.
+** 5.4.3 changes from Mark T. Robinson <mtr@ornl.gov>.
+*/
+
+#ifdef DGUX_5_4_2
+# define DGUX 1
+#endif
+
+#ifdef DGUX
+# define SYSTEM5 1
+# define LA_TYPE LA_DGUX
+# define HASSETREUID 1 /* has setreuid(2) call */
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASSETSID 1 /* has Posix setsid(2) call */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define IP_SRCROUTE 0 /* does not have <netinet/ip_var.h> */
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) */
+# define HASSNPRINTF 1 /* has snprintf(3) */
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+# define SPT_TYPE SPT_NONE /* don't use setproctitle */
+# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */
+
+/* these include files must be included early on DG/UX */
+# include <netinet/in.h>
+# include <arpa/inet.h>
+
+/* compiler doesn't understand const? */
+# define const
+
+# ifdef DGUX_5_4_2
+# define inet_addr dgux_inet_addr
+extern long dgux_inet_addr();
+# endif
+#endif
+
+
+/*
+** Digital Ultrix 4.2A or 4.3
+**
+** Apparently, fcntl locking is broken on 4.2A, in that locks are
+** not dropped when the process exits. This causes major problems,
+** so flock is the only alternative.
+*/
+
+#ifdef ultrix
+# define HASSETREUID 1 /* has setreuid(2) call */
+# define HASUNSETENV 1 /* has unsetenv(3) call */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# ifndef HASFLOCK
+# define HASFLOCK 1 /* has flock(2) call */
+# endif
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# ifndef BROKEN_RES_SEARCH
+# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */
+# endif
+# ifdef vax
+# define LA_TYPE LA_FLOAT
+# else
+# define LA_TYPE LA_INT
+# define LA_AVENRUN "avenrun"
+# endif
+# define SFS_TYPE SFS_MOUNT /* use <sys/mount.h> statfs() impl */
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* pre-4.4 TCP/IP implementation is broken */
+# endif
+# define SYSLOG_BUFSIZE 256
+#endif
+
+
+/*
+** OSF/1 for KSR.
+**
+** Contributed by Todd C. Miller <Todd.Miller@cs.colorado.edu>
+*/
+
+#ifdef __ksr__
+# define __osf__ 1 /* get OSF/1 defines below */
+# ifndef TZ_TYPE
+# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */
+# endif
+#endif
+
+
+/*
+** OSF/1 for Intel Paragon.
+**
+** Contributed by Jeff A. Earickson <jeff@ssd.intel.com>
+** of Intel Scalable Systems Divison.
+*/
+
+#ifdef __PARAGON__
+# define __osf__ 1 /* get OSF/1 defines below */
+# ifndef TZ_TYPE
+# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */
+# endif
+# define GIDSET_T gid_t
+# define MAXNAMLEN NAME_MAX
+#endif
+
+
+/*
+** OSF/1 (tested on Alpha) -- now known as Digital UNIX.
+**
+** Tested for 3.2 and 4.0.
+*/
+
+#ifdef __osf__
+# define HASUNSETENV 1 /* has unsetenv(3) call */
+# define USESETEUID 1 /* has useable seteuid(2) call */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define IP_SRCROUTE 1 /* can check IP source routing */
+# define HAS_ST_GEN 1 /* has st_gen field in stat struct */
+# ifndef HASFLOCK
+# define HASFLOCK 1 /* has flock(2) call */
+# endif
+# define LA_TYPE LA_ALPHAOSF
+# define SFS_TYPE SFS_STATVFS /* use <sys/statvfs.h> statfs() impl */
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/var/adm/sendmail/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/var/run/sendmail.pid"
+# endif
+# define bcopy(s, d, l) (memmove((d), (s), (l)))
+# define bzero(d, l) (memset((d), '\0', (l)))
+# define bcmp(s, d, l) (memcmp((s), (d), (l)))
+#endif
+
+
+/*
+** NeXTstep
+*/
+
+#ifdef NeXT
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define NEEDPUTENV 2 /* need putenv(3) call; no setenv(3) call */
+# ifndef HASFLOCK
+# define HASFLOCK 1 /* has flock(2) call */
+# endif
+# define NEEDGETOPT 1 /* need a replacement for getopt(3) */
+# define WAITUNION 1 /* use "union wait" as wait argument type */
+# define UID_T int /* compiler gripes on uid_t */
+# define GID_T int /* ditto for gid_t */
+# define MODE_T int /* and mode_t */
+# define setpgid setpgrp
+# ifndef NOT_SENDMAIL
+# define sleep sleepX
+# endif
+# ifndef LA_TYPE
+# define LA_TYPE LA_MACH
+# endif
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# ifndef _POSIX_SOURCE
+typedef int pid_t;
+# undef WEXITSTATUS
+# undef WIFEXITED
+# endif
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/etc/sendmail/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/sendmail/sendmail.pid"
+# endif
+
+# ifdef TCPWRAPPERS
+# ifndef HASUNSETENV
+# define HASUNSETENV 1
+# endif
+# undef NEEDPUTENV
+# endif
+
+#endif
+
+
+/*
+** 4.4 BSD
+**
+** See also BSD defines.
+*/
+
+#if defined(BSD4_4) && !defined(__bsdi__) && !defined(__GNU__)
+# include <paths.h>
+# define HASUNSETENV 1 /* has unsetenv(3) call */
+# define USESETEUID 1 /* has useable seteuid(2) call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */
+# define HASSTRERROR 1 /* has strerror(3) */
+# define HAS_ST_GEN 1 /* has st_gen field in stat struct */
+# include <sys/cdefs.h>
+# define ERRLIST_PREDEFINED /* don't declare sys_errlist */
+# define BSD4_4_SOCKADDR /* has sa_len */
+# define NEED_PRINTF_PERCENTQ 1 /* doesn't have %lld */
+# define NETLINK 1 /* supports AF_LINK */
+# ifndef LA_TYPE
+# define LA_TYPE LA_SUBR
+# endif
+# define SFS_TYPE SFS_MOUNT /* use <sys/mount.h> statfs() impl */
+# define SPT_TYPE SPT_PSSTRINGS /* use PS_STRINGS pointer */
+#endif
+
+
+/*
+** BSD/OS (was BSD/386) (all versions)
+** From Tony Sanders, BSDI
+*/
+
+#ifdef __bsdi__
+# include <paths.h>
+# define HASUNSETENV 1 /* has the unsetenv(3) call */
+# define HASSETSID 1 /* has the setsid(2) POSIX syscall */
+# define USESETEUID 1 /* has useable seteuid(2) call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */
+# define HASUNAME 1 /* has uname(2) syscall */
+# define HASSTRERROR 1 /* has strerror(3) */
+# define HAS_ST_GEN 1 /* has st_gen field in stat struct */
+# include <sys/cdefs.h>
+# define ERRLIST_PREDEFINED /* don't declare sys_errlist */
+# define BSD4_4_SOCKADDR /* has sa_len */
+# define NETLINK 1 /* supports AF_LINK */
+# define SFS_TYPE SFS_MOUNT /* use <sys/mount.h> statfs() impl */
+# ifndef LA_TYPE
+# define LA_TYPE LA_SUBR
+# endif
+# define GIDSET_T gid_t
+# define QUAD_T quad_t
+# if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199312
+ /* version 1.1 or later */
+# undef SPT_TYPE
+# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */
+# else
+ /* version 1.0 or earlier */
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+# endif
+# if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199701 /* on 3.x */
+# define HASSETUSERCONTEXT 1 /* has setusercontext */
+# endif
+#endif
+
+
+/*
+** QNX 4.2x
+** Contributed by Glen McCready <glen@qnx.com>.
+**
+** Should work with all versions of QNX.
+*/
+
+#if defined(__QNX__)
+# include <unix.h>
+# include <sys/select.h>
+# undef NGROUPS_MAX
+# define HASSETSID 1 /* has the setsid(2) POSIX syscall */
+# define USESETEUID 1 /* has useable seteuid(2) call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */
+# define HASSETREUID 1 /* has setreuid(2) call */
+# define HASSTRERROR 1 /* has strerror(3) */
+# define HASFLOCK 0
+# undef HASINITGROUPS /* has initgroups(3) call */
+# define NEEDGETOPT 1 /* use sendmail's getopt */
+# define IP_SRCROUTE 1 /* can check IP source routing */
+# define TZ_TYPE TZ_TMNAME /* use tmname variable */
+# define GIDSET_T gid_t
+# define LA_TYPE LA_ZERO
+# define SFS_TYPE SFS_NONE
+# define SPT_TYPE SPT_REUSEARGV
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+# define HASGETUSERSHELL 0
+# define E_PSEUDOBASE 512
+# define bcopy(s, d, l) (memmove((d), (s), (l)))
+# define bzero(d, l) (memset((d), '\0', (l)))
+# define bcmp(s, d, l) (memcmp((s), (d), (l)))
+# define _FILE_H_INCLUDED
+#endif
+
+
+/*
+** FreeBSD / NetBSD / OpenBSD (all architectures, all versions)
+**
+** 4.3BSD clone, closer to 4.4BSD for FreeBSD 1.x and NetBSD 0.9x
+** 4.4BSD-Lite based for FreeBSD 2.x and NetBSD 1.x
+**
+** See also BSD defines.
+*/
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# include <paths.h>
+# define HASUNSETENV 1 /* has unsetenv(3) call */
+# define HASSETSID 1 /* has the setsid(2) POSIX syscall */
+# define USESETEUID 1 /* has useable seteuid(2) call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */
+# define HASUNAME 1 /* has uname(2) syscall */
+# define HASSTRERROR 1 /* has strerror(3) */
+# define HAS_ST_GEN 1 /* has st_gen field in stat struct */
+# define NEED_PRINTF_PERCENTQ 1 /* doesn't have %lld */
+# include <sys/cdefs.h>
+# define ERRLIST_PREDEFINED /* don't declare sys_errlist */
+# define BSD4_4_SOCKADDR /* has sa_len */
+# define NETLINK 1 /* supports AF_LINK */
+# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */
+# define GIDSET_T gid_t
+# define QUAD_T unsigned long long
+# ifndef LA_TYPE
+# define LA_TYPE LA_SUBR
+# endif
+# define SFS_TYPE SFS_MOUNT /* use <sys/mount.h> statfs() impl */
+# if defined(__NetBSD__) && (NetBSD > 199307 || NetBSD0_9 > 1)
+# undef SPT_TYPE
+# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */
+# endif
+# if defined(__FreeBSD__)
+# undef SPT_TYPE
+# if __FreeBSD__ == 2
+# include <osreldate.h> /* and this works */
+# if __FreeBSD_version >= 199512 /* 2.2-current right now */
+# include <libutil.h>
+# define SPT_TYPE SPT_BUILTIN
+# endif
+# endif
+# ifndef SPT_TYPE
+# define SPT_TYPE SPT_REUSEARGV
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+# endif
+# endif
+# if defined(__OpenBSD__)
+# undef SPT_TYPE
+# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */
+# endif
+#endif
+
+
+
+/*
+** Mach386
+**
+** For mt Xinu's Mach386 system.
+*/
+
+#if defined(MACH) && defined(i386) && !defined(__GNU__)
+# define MACH386 1
+# define HASUNSETENV 1 /* has unsetenv(3) call */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# ifndef HASFLOCK
+# define HASFLOCK 1 /* has flock(2) call */
+# endif
+# define NEEDGETOPT 1 /* need a replacement for getopt(3) */
+# define NEEDSTRTOL 1 /* need the strtol() function */
+# define setpgid setpgrp
+# ifndef LA_TYPE
+# define LA_TYPE LA_FLOAT
+# endif
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# undef HASSETVBUF /* don't actually have setvbuf(3) */
+# undef WEXITSTATUS
+# undef WIFEXITED
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/sendmail.pid"
+# endif
+#endif
+
+
+
+/*
+** GNU OS (hurd)
+** Largely BSD & posix compatible.
+** Port contributed by Miles Bader <miles@gnu.ai.mit.edu>.
+*/
+
+#ifdef __GNU_HURD__
+# define SIOCGIFCONF_IS_BROKEN 1
+# define IP_SRCROUTE 0
+# define HASFCHMOD 1
+# define HASFLOCK 1
+# define HASUNAME 1
+# define HASUNSETENV 1
+# define HASSETSID 1
+# define HASINITGROUPS 1
+# define HASSETVBUF 1
+# define HASSETREUID 1
+# define USESETEUID 1
+# define HASLSTAT 1
+# define HASSETRLIMIT 1
+# define HASWAITPID 1
+# define HASGETDTABLESIZE 1
+# define HASSTRERROR 1
+/* # define NEEDGETOPT 1 */
+# define HASGETUSERSHELL 1
+# define ERRLIST_PREDEFINED 1
+# define BSD4_4_SOCKADDR 1
+# define GIDSET_T gid_t
+# define LA_TYPE LA_MACH
+
+/* GNU uses mach[34], which renames some rpcs from mach2.x. */
+# define host_self mach_host_self
+# define SFS_TYPE SFS_STATFS
+# define SPT_TYPE SPT_CHANGEARGV
+
+/* GNU has no MAXPATHLEN; ideally the code should be changed to not use it. */
+# define MAXPATHLEN 2048
+
+/* Define device num frobbing macros. */
+# define major(x) ((x)>>8)
+# define minor(x) ((x)&0xFF)
+#endif /* GNU */
+
+/*
+** 4.3 BSD -- this is for very old systems
+**
+** Should work for mt Xinu MORE/BSD and Mips UMIPS-BSD 2.1.
+**
+** You'll also have to install a new resolver library.
+** I don't guarantee that support for this environment is complete.
+*/
+
+#if defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd)
+# define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */
+# define NEEDGETOPT 1 /* need a replacement for getopt(3) */
+# define ARBPTR_T char *
+# define setpgid setpgrp
+# ifndef LA_TYPE
+# define LA_TYPE LA_FLOAT
+# endif
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+# undef WEXITSTATUS
+# undef WIFEXITED
+typedef short pid_t;
+extern int errno;
+#endif
+
+
+/*
+** SCO Unix
+**
+** This includes three parts:
+**
+** The first is for SCO OpenServer 5.
+** (Contributed by Keith Reynolds <keithr@sco.COM>).
+**
+** SCO OpenServer 5 has a compiler version number macro,
+** which we can use to figure out what version we're on.
+** This may have to change in future releases.
+**
+** The second is for SCO UNIX 3.2v4.2/Open Desktop 3.0.
+** (Contributed by Philippe Brand <phb@colombo.telesys-innov.fr>).
+**
+** The third is for SCO UNIX 3.2v4.0/Open Desktop 2.0 and earlier.
+*/
+
+/* SCO OpenServer 5 */
+#if _SCO_DS >= 1
+# include <paths.h>
+# define SIOCGIFNUM_IS_BROKEN 1 /* SIOCGIFNUM returns bogus value */
+# define HASSNPRINTF 1 /* has snprintf(3) call */
+# define HASFCHMOD 1 /* has fchmod(2) call */
+# define HASSETRLIMIT 1 /* has setrlimit(2) call */
+# define USESETEUID 1 /* has seteuid(2) call */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */
+# define RLIMIT_NEEDS_SYS_TIME_H 1
+# ifndef LA_TYPE
+# define LA_TYPE LA_DEVSHORT
+# endif
+# define _PATH_AVENRUN "/dev/table/avenrun"
+# ifndef _SCO_unix_4_2
+# define _SCO_unix_4_2
+# else
+# define SOCKADDR_LEN_T size_t /* e.g., arg#3 to accept, getsockname */
+# define SOCKOPT_LEN_T size_t /* arg#5 to getsockopt */
+# endif
+#endif
+
+/* SCO UNIX 3.2v4.2/Open Desktop 3.0 */
+#ifdef _SCO_unix_4_2
+# define _SCO_unix_
+# define HASSETREUID 1 /* has setreuid(2) call */
+#endif
+
+/* SCO UNIX 3.2v4.0 Open Desktop 2.0 and earlier */
+#ifdef _SCO_unix_
+# include <sys/stream.h> /* needed for IP_SRCROUTE */
+# define SYSTEM5 1 /* include all the System V defines */
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# define NOFTRUNCATE 0 /* has (simulated) ftruncate call */
+# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */
+# define MAXPATHLEN PATHSIZE
+# define SFS_TYPE SFS_4ARGS /* use <sys/statfs.h> 4-arg impl */
+# define SFS_BAVAIL f_bfree /* alternate field name */
+# define SPT_TYPE SPT_SCO /* write kernel u. area */
+# define TZ_TYPE TZ_TM_NAME /* use tm->tm_name */
+# define UID_T uid_t
+# define GID_T gid_t
+# define GIDSET_T gid_t
+# define _PATH_UNIX "/unix"
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/sendmail.pid"
+# endif
+
+/* stuff fixed in later releases */
+# ifndef _SCO_unix_4_2
+# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */
+# endif
+
+# ifndef _SCO_DS
+# define ftruncate chsize /* use chsize(2) to emulate ftruncate */
+# define NEEDFSYNC 1 /* needs the fsync(2) call stub */
+# define NETUNIX 0 /* no unix domain socket support */
+# define LA_TYPE LA_SHORT
+# endif
+
+#endif
+
+
+/*
+** ISC (SunSoft) Unix.
+**
+** Contributed by J.J. Bailey <jjb@jagware.bcc.com>
+*/
+
+#ifdef ISC_UNIX
+# include <net/errno.h>
+# include <sys/stream.h> /* needed for IP_SRCROUTE */
+# include <sys/bsdtypes.h>
+# define SYSTEM5 1 /* include all the System V defines */
+# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# define HASSETREUID 1 /* has setreuid(2) call */
+# define NEEDFSYNC 1 /* needs the fsync(2) call stub */
+# define NETUNIX 0 /* no unix domain socket support */
+# define MAXPATHLEN 1024
+# define LA_TYPE LA_SHORT
+# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */
+# define SFS_BAVAIL f_bfree /* alternate field name */
+# define _PATH_UNIX "/unix"
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/sendmail.pid"
+# endif
+
+#endif
+
+
+/*
+** Altos System V (5.3.1)
+** Contributed by Tim Rice <tim@trr.metro.net>.
+*/
+
+#ifdef ALTOS_SYSTEM_V
+# include <sys/stream.h>
+# include <limits.h>
+# define SYSTEM5 1 /* include all the System V defines */
+# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# define WAITUNION 1 /* use "union wait" as wait argument type */
+# define NEEDFSYNC 1 /* no fsync(2) in system library */
+# define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */
+# define NOFTRUNCATE 1 /* do not have ftruncate(2) */
+# define MAXPATHLEN PATH_MAX
+# define LA_TYPE LA_SHORT
+# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */
+# define SFS_BAVAIL f_bfree /* alternate field name */
+# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */
+# define NETUNIX 0 /* no unix domain socket support */
+# undef WIFEXITED
+# undef WEXITSTATUS
+# define strtoul strtol /* gcc library bogosity */
+
+typedef unsigned short uid_t;
+typedef unsigned short gid_t;
+typedef short pid_t;
+typedef unsigned long mode_t;
+
+/* some stuff that should have been in the include files */
+# include <grp.h>
+extern char *malloc();
+extern struct passwd *getpwent();
+extern struct passwd *getpwnam();
+extern struct passwd *getpwuid();
+extern char *getenv();
+extern struct group *getgrgid();
+extern struct group *getgrnam();
+
+#endif
+
+
+/*
+** ConvexOS 11.0 and later
+**
+** "Todd C. Miller" <millert@mroe.cs.colorado.edu> claims this
+** works on 9.1 as well.
+**
+** ConvexOS 11.5 and later, should work on 11.0 as defined.
+** For pre-ConvexOOS 11.0, define NEEDGETOPT, undef IDENTPROTO
+**
+** Eric Schnoebelen (eric@cirr.com) For CONVEX Computer Corp.
+** (now the CONVEX Technologies Center of Hewlett Packard)
+*/
+
+#ifdef _CONVEX_SOURCE
+# define HASGETDTABLESIZE 1 /* has getdtablesize(2) */
+# define HASINITGROUPS 1 /* has initgroups(3) */
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASSETSID 1 /* has POSIX setsid(2) call */
+# define HASUNSETENV 1 /* has unsetenv(3) */
+# define HASFLOCK 1 /* has flock(2) */
+# define HASSETRLIMIT 1 /* has setrlimit(2) */
+# define HASSETREUID 1 /* has setreuid(2) */
+# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_error=0 */
+# define NEEDPUTENV 1 /* needs putenv (written in terms of setenv) */
+# define NEEDGETOPT 0 /* need replacement for getopt(3) */
+# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */
+# define LA_TYPE LA_FLOAT
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# ifndef S_IREAD
+# define S_IREAD _S_IREAD
+# define S_IWRITE _S_IWRITE
+# define S_IEXEC _S_IEXEC
+# define S_IFMT _S_IFMT
+# define S_IFCHR _S_IFCHR
+# define S_IFBLK _S_IFBLK
+# endif
+# ifndef TZ_TYPE
+# define TZ_TYPE TZ_TIMEZONE
+# endif
+# ifndef IDENTPROTO
+# define IDENTPROTO 1
+# endif
+# ifndef SHARE_V1
+# define SHARE_V1 1 /* version 1 of the fair share scheduler */
+# endif
+# if !defined(__GNUC__ )
+# define UID_T int /* GNUC gets it right, ConvexC botches */
+# define GID_T int /* GNUC gets it right, ConvexC botches */
+# endif
+# if SECUREWARE
+# define FORK fork /* SecureWare wants the real fork! */
+# else
+# define FORK vfork /* the rest of the OS versions don't care */
+# endif
+#endif
+
+
+/*
+** RISC/os 4.52
+**
+** Gives a ton of warning messages, but otherwise compiles.
+*/
+
+#ifdef RISCOS
+
+# define HASUNSETENV 1 /* has unsetenv(3) call */
+# ifndef HASFLOCK
+# define HASFLOCK 1 /* has flock(2) call */
+# endif
+# define WAITUNION 1 /* use "union wait" as wait argument type */
+# define NEEDGETOPT 1 /* need a replacement for getopt(3) */
+# define NEEDPUTENV 1 /* need putenv(3) call */
+# define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# define LA_TYPE LA_INT
+# define LA_AVENRUN "avenrun"
+# define _PATH_UNIX "/unix"
+# undef WIFEXITED
+
+# define setpgid setpgrp
+
+extern int errno;
+typedef int pid_t;
+# define SIGFUNC_DEFINED
+# define SIGFUNC_RETURN (0)
+# define SIGFUNC_DECL int
+typedef int (*sigfunc_t)();
+extern char *getenv();
+extern void *malloc();
+
+/* added for RISC/os 4.01...which is dumber than 4.50 */
+# ifdef RISCOS_4_0
+# ifndef ARBPTR_T
+# define ARBPTR_T char *
+# endif
+# undef HASFLOCK
+# define HASFLOCK 0
+# endif /* RISCOS_4_0 */
+
+# include <sys/time.h>
+
+#endif
+
+
+/*
+** Linux 0.99pl10 and above...
+**
+** Thanks to, in reverse order of contact:
+**
+** John Kennedy <warlock@csuchico.edu>
+** Andrew Pam <avatar@aus.xanadu.com>
+** Florian La Roche <rzsfl@rz.uni-sb.de>
+** Karl London <karl@borg.demon.co.uk>
+**
+** Last compiled against: [06/10/96 @ 09:21:40 PM (Monday)]
+** sendmail 8.8-a4 named bind-4.9.4-T4B db-1.85
+** gcc 2.7.2 libc-5.3.12 linux 2.0.0
+**
+** NOTE: Override HASFLOCK as you will but, as of 1.99.6, mixed-style
+** file locking is no longer allowed. In particular, make sure
+** your DBM library and sendmail are both using either flock(2)
+** *or* fcntl(2) file locking, but not both.
+*/
+
+#ifdef __linux__
+# define BSD 1 /* include BSD defines */
+# define NEEDGETOPT 1 /* need a replacement for getopt(3) */
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASUNSETENV 1 /* has unsetenv(3) call */
+# ifndef HASSNPRINTF
+# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */
+# endif
+# define ERRLIST_PREDEFINED /* don't declare sys_errlist */
+# define GIDSET_T gid_t /* from <linux/types.h> */
+# define HASGETUSERSHELL 0 /* getusershell(3) broken in Slackware 2.0 */
+# define IP_SRCROUTE 0 /* linux <= 1.2.8 doesn't support IP_OPTIONS */
+# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */
+# ifndef HASFLOCK
+# include <linux/version.h>
+# if LINUX_VERSION_CODE < 66399
+# define HASFLOCK 0 /* flock(2) is broken after 0.99.13 */
+# else
+# define HASFLOCK 1 /* flock(2) fixed after 1.3.95 */
+# endif
+# endif
+# ifndef LA_TYPE
+# define LA_TYPE LA_PROCSTR
+# endif
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() impl */
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/var/run/sendmail.pid"
+# endif
+# define TZ_TYPE TZ_TNAME
+# include <sys/sysmacros.h>
+# undef atol /* wounded in <stdlib.h> */
+#endif
+
+
+/*
+** DELL SVR4 Issue 2.2, and others
+** From Kimmo Suominen <kim@grendel.lut.fi>
+**
+** It's on #ifdef DELL_SVR4 because Solaris also gets __svr4__
+** defined, and the definitions conflict.
+**
+** Peter Wemm <peter@perth.DIALix.oz.au> claims that the setreuid
+** trick works on DELL 2.2 (SVR4.0/386 version 4.0) and ESIX 4.0.3A
+** (SVR4.0/386 version 3.0).
+*/
+
+#ifdef DELL_SVR4
+ /* no changes necessary */
+ /* see general __svr4__ defines below */
+#endif
+
+
+/*
+** Apple A/UX 3.0
+*/
+
+#ifdef _AUX_SOURCE
+# include <sys/sysmacros.h>
+# define BSD /* has BSD routines */
+# define HASSETRLIMIT 0 /* ... but not setrlimit(2) */
+# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */
+# define BOGUS_O_EXCL 1 /* exclusive open follows symlinks */
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASFCHMOD 1 /* has fchmod(2) syscall */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASSETVBUF 1 /* has setvbuf(3) in libc */
+# define HASSTRERROR 1 /* has strerror(3) */
+# define SIGFUNC_DEFINED /* sigfunc_t already defined */
+# define SIGFUNC_RETURN /* POSIX-mode */
+# define SIGFUNC_DECL void /* POSIX-mode */
+# define ERRLIST_PREDEFINED 1
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+# ifndef LA_TYPE
+# define LA_TYPE LA_INT
+# define FSHIFT 16
+# endif
+# define LA_AVENRUN "avenrun"
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# define TZ_TYPE TZ_TZNAME
+# ifndef _PATH_UNIX
+# define _PATH_UNIX "/unix" /* should be in <paths.h> */
+# endif
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# undef WIFEXITED
+# undef WEXITSTATUS
+#endif
+
+
+/*
+** Encore UMAX V
+**
+** Not extensively tested.
+*/
+
+#ifdef UMAXV
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASSETVBUF 1 /* we have setvbuf(3) in libc */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */
+# define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */
+# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */
+# define MAXPATHLEN PATH_MAX
+extern struct passwd *getpwent(), *getpwnam(), *getpwuid();
+extern struct group *getgrent(), *getgrnam(), *getgrgid();
+# undef WIFEXITED
+# undef WEXITSTATUS
+#endif
+
+
+/*
+** Stardent Titan 3000 running TitanOS 4.2.
+**
+** Must be compiled in "cc -43" mode.
+**
+** From Kate Hedstrom <kate@ahab.rutgers.edu>.
+**
+** Note the tweaking below after the BSD defines are set.
+*/
+
+#ifdef titan
+# define setpgid setpgrp
+typedef int pid_t;
+# undef WIFEXITED
+# undef WEXITSTATUS
+#endif
+
+
+/*
+** Sequent DYNIX 3.2.0
+**
+** From Jim Davis <jdavis@cs.arizona.edu>.
+*/
+
+#ifdef sequent
+
+# define BSD 1
+# define HASUNSETENV 1
+# define BSD4_3 1 /* to get signal() in conf.c */
+# define WAITUNION 1
+# define LA_TYPE LA_FLOAT
+# ifdef _POSIX_VERSION
+# undef _POSIX_VERSION /* set in <unistd.h> */
+# endif
+# undef HASSETVBUF /* don't actually have setvbuf(3) */
+# define setpgid setpgrp
+
+/* Have to redefine WIFEXITED to take an int, to work with waitfor() */
+# undef WIFEXITED
+# define WIFEXITED(s) (((union wait*)&(s))->w_stopval != WSTOPPED && \
+ ((union wait*)&(s))->w_termsig == 0)
+# define WEXITSTATUS(s) (((union wait*)&(s))->w_retcode)
+typedef int pid_t;
+# define isgraph(c) (isprint(c) && (c != ' '))
+
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+
+# ifndef _PATH_UNIX
+# define _PATH_UNIX "/dynix"
+# endif
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+#endif
+
+
+/*
+** Sequent DYNIX/ptx v2.0 (and higher)
+**
+** For DYNIX/ptx v1.x, undefine HASSETREUID.
+**
+** From Tim Wright <timw@sequent.com>.
+** Update from Jack Woolley <jwoolley@sctcorp.com>, 26 Dec 1995,
+** for DYNIX/ptx 4.0.2.
+*/
+
+#ifdef _SEQUENT_
+# include <sys/stream.h>
+# define SYSTEM5 1 /* include all the System V defines */
+# define HASSETSID 1 /* has POSIX setsid(2) call */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define HASSETREUID 1 /* has setreuid(2) call */
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# define GIDSET_T gid_t
+# define LA_TYPE LA_INT
+# define SFS_TYPE SFS_STATFS /* use <sys/statfs.h> statfs() impl */
+# define SPT_TYPE SPT_NONE /* don't use setproctitle */
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/sendmail.pid"
+# endif
+#endif
+
+
+/*
+** Cray Unicos
+**
+** Ported by David L. Kensiski, Sterling Sofware <kensiski@nas.nasa.gov>
+*/
+
+#ifdef UNICOS
+# define SYSTEM5 1 /* include all the System V defines */
+# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */
+# define MAXPATHLEN PATHSIZE
+# define LA_TYPE LA_ZERO
+# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */
+# define SFS_BAVAIL f_bfree /* alternate field name */
+#endif
+
+
+/*
+** Apollo DomainOS
+**
+** From Todd Martin <tmartint@tus.ssi1.com> & Don Lewis <gdonl@gv.ssi1.com>
+**
+** 15 Jan 1994; updated 2 Aug 1995
+**
+*/
+
+#ifdef apollo
+# define HASSETREUID 1 /* has setreuid(2) call */
+# define HASINITGROUPS 1 /* has initgroups(2) call */
+# define IP_SRCROUTE 0 /* does not have <netinet/ip_var.h> */
+# define SPT_TYPE SPT_NONE /* don't use setproctitle */
+# define LA_TYPE LA_SUBR /* use getloadavg.c */
+# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */
+# define SFS_BAVAIL f_bfree /* alternate field name */
+# define TZ_TYPE TZ_TZNAME
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/sendmail.pid"
+# endif
+# undef S_IFSOCK /* S_IFSOCK and S_IFIFO are the same */
+# undef S_IFIFO
+# define S_IFIFO 0010000
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+# define RLIMIT_NEEDS_SYS_TIME_H 1
+# if defined(NGROUPS_MAX) && !NGROUPS_MAX
+# undef NGROUPS_MAX
+# endif
+#endif
+
+
+/*
+** UnixWare 2.x
+*/
+
+#ifdef UNIXWARE2
+# define UNIXWARE 1
+# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */
+# undef offsetof /* avoid stddefs.h, sys/sysmacros.h conflict */
+#endif
+
+
+/*
+** UnixWare 1.1.2.
+**
+** Updated by Petr Lampa <lampa@fee.vutbr.cz>.
+** From Evan Champion <evanc@spatial.synapse.org>.
+*/
+
+#ifdef UNIXWARE
+# include <sys/mkdev.h>
+# define SYSTEM5 1
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# define HASSETREUID 1
+# define HASSETSID 1
+# define HASINITGROUPS 1
+# define GIDSET_T gid_t
+# define SLEEP_T unsigned
+# define SFS_TYPE SFS_STATVFS
+# define LA_TYPE LA_ZERO
+# undef WIFEXITED
+# undef WEXITSTATUS
+# define _PATH_UNIX "/unix"
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid"
+# endif
+# define SYSLOG_BUFSIZE 128
+#endif
+
+
+/*
+** Intergraph CLIX 3.1
+**
+** From Paul Southworth <pauls@locust.cic.net>
+*/
+
+#ifdef CLIX
+# define SYSTEM5 1 /* looks like System V */
+# ifndef HASGETUSERSHELL
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# endif
+# define DEV_BSIZE 512 /* device block size not defined */
+# define GIDSET_T gid_t
+# undef LOG /* syslog not available */
+# define NEEDFSYNC 1 /* no fsync in system library */
+# define GETSHORT _getshort
+#endif
+
+
+/*
+** NCR MP-RAS 2.x (SysVr4) with Wollongong TCP/IP
+**
+** From Kevin Darcy <kevin@tech.mis.cfc.com>.
+*/
+
+#ifdef NCR_MP_RAS2
+# include <sys/sockio.h>
+# define __svr4__
+# define IP_SRCROUTE 0 /* Something is broken with getsockopt() */
+# define SYSLOG_BUFSIZE 1024
+# define SPT_TYPE SPT_NONE
+#endif
+
+
+/*
+** NCR MP-RAS 3.x (SysVr4) with STREAMware TCP/IP
+**
+** From Tom Moore <Tom.Moore@DaytonOH.NCR.COM>
+*/
+
+#ifdef NCR_MP_RAS3
+# define __svr4__
+# define SIOCGIFNUM_IS_BROKEN 1 /* SIOCGIFNUM has non-std interface */
+# define SYSLOG_BUFSIZE 1024
+# define SPT_TYPE SPT_NONE
+#endif
+
+
+/*
+** Tandem NonStop-UX SVR4
+**
+** From Rick McCarty <mccarty@mpd.tandem.com>.
+*/
+
+#ifdef NonStop_UX_BXX
+# define __svr4__
+#endif
+
+
+/*
+** Hitachi 3050R & 3050RX Workstations running HI-UX/WE2.
+**
+** Tested for 1.04 and 1.03
+** From Akihiro Hashimoto ("Hash") <hash@dominic.ipc.chiba-u.ac.jp>.
+*/
+
+#ifdef __H3050R
+# define SYSTEM5 1 /* include all the System V defines */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define setreuid(r, e) setresuid(r, e, -1)
+# define LA_TYPE LA_FLOAT
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# define HASSETVBUF /* HI-UX has no setlinebuf */
+# ifndef GIDSET_T
+# define GIDSET_T gid_t
+# endif
+# ifndef _PATH_UNIX
+# define _PATH_UNIX "/HI-UX"
+# endif
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+# ifndef HASGETUSERSHELL
+# define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */
+# endif
+
+/*
+** avoid m_flags conflict between Berkeley DB 1.85 db.h & sys/sysmacros.h
+** on HIUX 3050
+*/
+# undef m_flags
+
+# ifdef __STDC__
+extern int syslog(int, char *, ...);
+#else
+extern int syslog();
+# endif
+
+#endif
+
+
+/*
+** Amdahl UTS System V 2.1.5 (SVr3-based)
+**
+** From: Janet Jackson <janet@dialix.oz.au>.
+*/
+
+#ifdef _UTS
+# include <sys/sysmacros.h>
+# undef HASLSTAT /* has symlinks, but they cause problems */
+# define NEEDFSYNC 1 /* system fsync(2) fails on non-EFS filesys */
+# define SYS5SIGNALS 1 /* System V signal semantics */
+# define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASINITGROUPS 1 /* has initgroups(3) function */
+# define HASSETVBUF 1 /* has setvbuf(3) function */
+# ifndef HASGETUSERSHELL
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) function */
+# endif
+# define GIDSET_T gid_t /* type of 2nd arg to getgroups(2) isn't int */
+# define LA_TYPE LA_ZERO /* doesn't have load average */
+# define SFS_TYPE SFS_4ARGS /* use 4-arg statfs() */
+# define SFS_BAVAIL f_bfree /* alternate field name */
+# define _PATH_UNIX "/unix"
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+#endif
+
+/*
+** Cray Computer Corporation's CSOS
+**
+** From Scott Bolte <scott@craycos.com>.
+*/
+
+#ifdef _CRAYCOM
+# define SYSTEM5 1 /* include all the System V defines */
+# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */
+# define NEEDFSYNC 1 /* no fsync in system library */
+# define MAXPATHLEN PATHSIZE
+# define LA_TYPE LA_ZERO
+# define SFS_TYPE SFS_4ARGS /* four argument statfs() call */
+# define SFS_BAVAIL f_bfree /* alternate field name */
+# define _POSIX_CHOWN_RESTRICTED -1
+extern struct group *getgrent(), *getgrnam(), *getgrgid();
+#endif
+
+
+/*
+** Sony NEWS-OS 4.2.1R and 6.0.3
+**
+** From Motonori NAKAMURA <motonori@cs.ritsumei.ac.jp>.
+*/
+
+#ifdef sony_news
+# ifndef __svr4
+ /* NEWS-OS 4.2.1R */
+# ifndef BSD
+# define BSD /* has BSD routines */
+# endif
+# define HASUNSETENV 1 /* has unsetenv(2) call */
+# undef HASSETVBUF /* don't actually have setvbuf(3) */
+# define WAITUNION 1 /* use "union wait" as wait argument type */
+# define LA_TYPE LA_INT
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# ifndef HASFLOCK
+# define HASFLOCK 1 /* has flock(2) call */
+# endif
+# define setpgid setpgrp
+# undef WIFEXITED
+# undef WEXITSTATUS
+# define MODE_T int /* system include files have no mode_t */
+typedef int pid_t;
+typedef int (*sigfunc_t)();
+# define SIGFUNC_DEFINED
+# define SIGFUNC_RETURN (0)
+# define SIGFUNC_DECL int
+
+# else
+ /* NEWS-OS 6.0.3 with /bin/cc */
+# ifndef __svr4__
+# define __svr4__ /* use all System V Releae 4 defines below */
+# endif
+# define HASSETSID 1 /* has Posix setsid(2) call */
+# define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */
+# define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */
+# ifndef SPT_TYPE
+# define SPT_TYPE SPT_SYSMIPS /* use sysmips() (OS 6.0.2 or later) */
+# endif
+# define GIDSET_T gid_t
+# undef WIFEXITED
+# undef WEXITSTATUS
+# ifndef SYSLOG_BUFSIZE
+# define SYSLOG_BUFSIZE 1024
+# endif
+# define _PATH_UNIX "/stand/unix"
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid"
+# endif
+
+# endif
+#endif
+
+
+/*
+** Omron LUNA/UNIOS-B 3.0, LUNA2/Mach and LUNA88K Mach
+**
+** From Motonori NAKAMURA <motonori@cs.ritsumei.ac.jp>.
+*/
+
+#ifdef luna
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+# define HASUNSETENV 1 /* has unsetenv(2) call */
+# define NEEDPUTENV 1 /* need putenv(3) call */
+# define NEEDGETOPT 1 /* need a replacement for getopt(3) */
+# define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */
+# define WAITUNION 1 /* use "union wait" as wait argument type */
+# ifdef uniosb
+# include <sys/time.h>
+# define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */
+# define LA_TYPE LA_INT
+# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */
+# endif
+# ifdef luna2
+# define LA_TYPE LA_SUBR
+# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */
+# endif
+# ifdef luna88k
+# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */
+# define LA_TYPE LA_INT
+# endif
+# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() implementation */
+# define setpgid setpgrp
+# undef WIFEXITED
+# undef WEXITSTATUS
+typedef int pid_t;
+typedef int (*sigfunc_t)();
+# define SIGFUNC_DEFINED
+# define SIGFUNC_RETURN (0)
+# define SIGFUNC_DECL int
+extern char *getenv();
+extern int errno;
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/lib/sendmail.cf"
+# endif
+#endif
+
+
+/*
+** NEC EWS-UX/V 4.2 (with /usr/ucb/cc)
+**
+** From Motonori NAKAMURA <motonori@cs.ritsumei.ac.jp>.
+*/
+
+#if defined(nec_ews_svr4) || defined(_nec_ews_svr4)
+# ifndef __svr4__
+# define __svr4__ /* use all System V Releae 4 defines below */
+# endif
+# define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */
+# define HASSETSID 1 /* has Posix setsid(2) call */
+# define LA_TYPE LA_READKSYM /* use MIOC_READSYM ioctl */
+# define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */
+# define GIDSET_T gid_t
+# undef WIFEXITED
+# undef WEXITSTATUS
+# define NAMELISTMASK 0x7fffffff /* mask for nlist() values */
+# ifndef SYSLOG_BUFSIZE
+# define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */
+# endif
+#endif
+
+
+/*
+** Fujitsu/ICL UXP/DS (For the DS/90 Series)
+**
+** From Diego R. Lopez <drlopez@cica.es>.
+** Additional changes from Fumio Moriya and Toshiaki Nomura of the
+** Fujitsu Fresoftware gruop <dsfrsoft@oai6.yk.fujitsu.co.jp>.
+*/
+
+#ifdef __uxp__
+# include <arpa/nameser.h>
+# include <sys/sysmacros.h>
+# include <sys/mkdev.h>
+# define __svr4__
+# define HASGETUSERSHELL 0
+# define HASFLOCK 0
+# if UXPDS == 10
+# define HASSNPRINTF 0 /* no snprintf(3) or vsnprintf(3) */
+# else
+# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */
+# endif
+# define _PATH_UNIX "/stand/unix"
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid"
+# endif
+#endif
+
+/*
+** Pyramid DC/OSx
+**
+** From Earle Ake <akee@wpdiss1.wpafb.af.mil>.
+*/
+
+#ifdef DCOSx
+# define GIDSET_T gid_t
+# ifndef IDENTPROTO
+# define IDENTPROTO 0 /* TCP/IP implementation is broken */
+# endif
+#endif
+
+/*
+** Concurrent Computer Corporation Maxion
+**
+** From Donald R. Laster Jr. <laster@access.digex.net>.
+*/
+
+#ifdef __MAXION__
+
+# include <sys/stream.h>
+# define __svr4__ 1 /* SVR4.2MP */
+# define HASSETREUID 1 /* have setreuid(2) */
+# define HASLSTAT 1 /* have lstat(2) */
+# define HASSETRLIMIT 1 /* have setrlimit(2) */
+# define HASGETDTABLESIZE 1 /* have getdtablesize(2) */
+# define HASSNPRINTF 1 /* have snprintf(3) */
+# define HASGETUSERSHELL 1 /* have getusershell(3) */
+# define NOFTRUNCATE 1 /* do not have ftruncate(2) */
+# define SLEEP_T unsigned
+# define SFS_TYPE SFS_STATVFS
+# define SFS_BAVAIL f_bavail
+# ifndef SYSLOG_BUFSIZE
+# define SYSLOG_BUFSIZE 256 /* Use 256 bytes */
+# endif
+
+# undef WUNTRACED
+# undef WIFEXITED
+# undef WIFSIGNALED
+# undef WIFSTOPPED
+# undef WEXITSTATUS
+# undef WTERMSIG
+# undef WSTOPSIG
+
+#endif
+
+/*
+** Harris Nighthawk PowerUX (nh6000 box)
+**
+** Contributed by Bob Miorelli, Pratt & Whitney <miorelli@pweh.com>
+*/
+
+#ifdef _PowerUX
+# ifndef __svr4__
+# define __svr4__
+# endif
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/etc/mail/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/etc/mail/sendmail.pid"
+# endif
+# define SYSLOG_BUFSIZE 1024
+# define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */
+# define LA_TYPE LA_ZERO
+typedef struct msgb mblk_t;
+# undef offsetof /* avoid stddefs.h and sys/sysmacros.h conflict */
+#endif
+
+/*
+** Siemens Nixdorf Informationssysteme AG SINIX
+**
+** Contributed by Gerald Rinske <Gerald.Rinske@mch.sni.de>
+** of Siemens Business Services VAS.
+*/
+#ifdef sinix
+# define SYSLOG_BUFSIZE 1024
+#endif
+
+/*
+** CRAY T3E
+**
+** Contributed by Manu Mahonen <mailadm@csc.fi>
+** of Center for Scientific Computing.
+*/
+#ifdef _CRAY
+# define GET_IPOPT_DST(dst) *(struct in_addr *)&(dst)
+#endif
+
+/**********************************************************************
+** End of Per-Operating System defines
+**********************************************************************/
+ /**********************************************************************
+** More general defines
+**********************************************************************/
+
+/* general BSD defines */
+#ifdef BSD
+# define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */
+# define HASSETREUID 1 /* has setreuid(2) call */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# ifndef IP_SRCROUTE
+# define IP_SRCROUTE 1 /* can check IP source routing */
+# endif
+# ifndef HASSETRLIMIT
+# define HASSETRLIMIT 1 /* has setrlimit(2) call */
+# endif
+# ifndef HASFLOCK
+# define HASFLOCK 1 /* has flock(2) call */
+# endif
+# ifndef TZ_TYPE
+# define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone variable */
+# endif
+#endif
+
+/* general System V Release 4 defines */
+#ifdef __svr4__
+# define SYSTEM5 1
+# define USESETEUID 1 /* has useable seteuid(2) call */
+# define HASINITGROUPS 1 /* has initgroups(3) call */
+# define BSD_COMP 1 /* get BSD ioctl calls */
+# ifndef HASSETRLIMIT
+# define HASSETRLIMIT 1 /* has setrlimit(2) call */
+# endif
+# ifndef HASGETUSERSHELL
+# define HASGETUSERSHELL 0 /* does not have getusershell(3) call */
+# endif
+# ifndef HASFCHMOD
+# define HASFCHMOD 1 /* most (all?) SVr4s seem to have fchmod(2) */
+# endif
+
+# ifndef _PATH_UNIX
+# define _PATH_UNIX "/unix"
+# endif
+# ifndef _PATH_VENDOR_CF
+# define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf"
+# endif
+# ifndef _PATH_SENDMAILPID
+# define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid"
+# endif
+# ifndef SYSLOG_BUFSIZE
+# define SYSLOG_BUFSIZE 128
+# endif
+# ifndef SFS_TYPE
+# define SFS_TYPE SFS_STATVFS
+# endif
+
+# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */
+#endif
+
+/* general System V defines */
+#ifdef SYSTEM5
+# include <sys/sysmacros.h>
+# define HASUNAME 1 /* use System V uname(2) system call */
+# define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */
+# define HASSETVBUF 1 /* we have setvbuf(3) in libc */
+# ifndef HASULIMIT
+# define HASULIMIT 1 /* has the ulimit(2) syscall */
+# endif
+# ifndef LA_TYPE
+# ifdef MIOC_READKSYM
+# define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */
+# else
+# define LA_TYPE LA_INT /* assume integer load average */
+# endif
+# endif
+# ifndef SFS_TYPE
+# define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */
+# endif
+# ifndef TZ_TYPE
+# define TZ_TYPE TZ_TZNAME /* use tzname[] vector */
+# endif
+# define bcopy(s, d, l) (memmove((d), (s), (l)))
+# define bzero(d, l) (memset((d), '\0', (l)))
+# define bcmp(s, d, l) (memcmp((s), (d), (l)))
+#endif
+
+/* general POSIX defines */
+#ifdef _POSIX_VERSION
+# define HASSETSID 1 /* has Posix setsid(2) call */
+# define HASWAITPID 1 /* has Posix waitpid(2) call */
+# if _POSIX_VERSION >= 199500 && !defined(USESETEUID)
+# define USESETEUID 1 /* has useable seteuid(2) call */
+# endif
+# ifndef bcopy
+# define bcopy(s, d, l) (memmove((d), (s), (l)))
+# define bzero(d, l) (memset((d), '\0', (l)))
+# define bcmp(s, d, l) (memcmp((s), (d), (l)))
+# endif
+#endif
+ /*
+** Tweaking for systems that (for example) claim to be BSD or POSIX
+** but don't have all the standard BSD or POSIX routines (boo hiss).
+*/
+
+#ifdef titan
+# undef HASINITGROUPS /* doesn't have initgroups(3) call */
+#endif
+
+#ifdef _CRAYCOM
+# undef HASSETSID /* despite POSIX claim, doesn't have setsid */
+#endif
+
+#ifdef ISC_UNIX
+# undef bcopy /* despite SystemV claim, uses BSD bcopy */
+#endif
+
+#ifdef ALTOS_SYSTEM_V
+# undef bcopy /* despite SystemV claim, uses BSD bcopy */
+# undef bzero /* despite SystemV claim, uses BSD bzero */
+# undef bcmp /* despite SystemV claim, uses BSD bcmp */
+#endif
+
+#if defined(sun) && !defined(BSD) && !defined(SOLARIS) && !defined(__svr4__) && !defined(__SVR4)
+# undef bcopy /* SunOS 4 doesn't have memmove() */
+#endif
+
+
+/*
+** Due to a "feature" in some operating systems such as Ultrix 4.3 and
+** HPUX 8.0, if you receive a "No route to host" message (ICMP message
+** ICMP_UNREACH_HOST) on _any_ connection, all connections to that host
+** are closed. Some firewalls return this error if you try to connect
+** to the IDENT port (113), so you can't receive email from these hosts
+** on these systems. The firewall really should use a more specific
+** message such as ICMP_UNREACH_PROTOCOL or _PORT or _FILTER_PROHIB. If
+** not explicitly set to zero above, default it on.
+*/
+
+#ifndef IDENTPROTO
+# define IDENTPROTO 1 /* use IDENT proto (RFC 1413) */
+#endif
+
+#ifndef IP_SRCROUTE
+# define IP_SRCROUTE 1 /* Detect IP source routing */
+#endif
+
+#ifndef HASGETUSERSHELL
+# define HASGETUSERSHELL 1 /* libc has getusershell(3) call */
+#endif
+
+#ifndef NETUNIX
+# define NETUNIX 1 /* include unix domain support */
+#endif
+
+#ifndef HASFLOCK
+# define HASFLOCK 0 /* assume no flock(2) support */
+#endif
+
+#ifndef HASSETREUID
+# define HASSETREUID 0 /* assume no setreuid(2) call */
+#endif
+
+#ifndef HASFCHMOD
+# define HASFCHMOD 0 /* assume no fchmod(2) syscall */
+#endif
+
+#ifndef USESETEUID
+# define USESETEUID 0 /* assume no seteuid(2) call or no saved ids */
+#endif
+
+#ifndef HASSETRLIMIT
+# define HASSETRLIMIT 0 /* assume no setrlimit(2) support */
+#endif
+
+#ifndef HASULIMIT
+# define HASULIMIT 0 /* assume no ulimit(2) support */
+#endif
+
+#ifndef SECUREWARE
+# define SECUREWARE 0 /* assume no SecureWare C2 auditing hooks */
+#endif
+
+#ifndef USE_SIGLONGJMP
+# define USE_SIGLONGJMP 0 /* assume setjmp handles signals properly */
+#endif
+
+#ifndef FDSET_CAST
+# define FDSET_CAST /* (empty) cast for fd_set arg to select */
+#endif
+
+/*
+** If no type for argument two of getgroups call is defined, assume
+** it's an integer -- unfortunately, there seem to be several choices
+** here.
+*/
+
+#ifndef GIDSET_T
+# define GIDSET_T int
+#endif
+
+#ifndef UID_T
+# define UID_T uid_t
+#endif
+
+#ifndef GID_T
+# define GID_T gid_t
+#endif
+
+#ifndef SIZE_T
+# define SIZE_T size_t
+#endif
+
+#ifndef MODE_T
+# define MODE_T mode_t
+#endif
+
+#ifndef ARGV_T
+# define ARGV_T char **
+#endif
+
+#ifndef SOCKADDR_LEN_T
+# define SOCKADDR_LEN_T int
+#endif
+
+#ifndef SOCKOPT_LEN_T
+# define SOCKOPT_LEN_T int
+#endif
+
+#ifndef QUAD_T
+# define QUAD_T unsigned long
+#endif
+ /**********************************************************************
+** Remaining definitions should never have to be changed. They are
+** primarily to provide back compatibility for older systems -- for
+** example, it includes some POSIX compatibility definitions
+**********************************************************************/
+
+/* System 5 compatibility */
+#ifndef S_ISREG
+# define S_ISREG(foo) ((foo & S_IFMT) == S_IFREG)
+#endif
+#ifndef S_ISDIR
+# define S_ISDIR(foo) ((foo & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+# define S_ISLNK(foo) ((foo & S_IFMT) == S_IFLNK)
+#endif
+#ifndef S_IRUSR
+# define S_IRUSR 0400
+#endif
+#ifndef S_IWUSR
+# define S_IWUSR 0200
+#endif
+#ifndef S_IRGRP
+# define S_IRGRP 0040
+#endif
+#ifndef S_IWGRP
+# define S_IWGRP 0020
+#endif
+#ifndef S_IROTH
+# define S_IROTH 0004
+#endif
+#ifndef S_IWOTH
+# define S_IWOTH 0002
+#endif
+
+/*
+** Older systems don't have this error code -- it should be in
+** /usr/include/sysexits.h.
+*/
+
+# ifndef EX_CONFIG
+# define EX_CONFIG 78 /* configuration error */
+# endif
+
+/* pseudo-code used in server SMTP */
+# define EX_QUIT 22 /* drop out of server immediately */
+
+/* pseudo-code used for mci_setstat */
+# define EX_NOTSTICKY -5 /* don't save persistent status */
+
+
+/*
+** An "impossible" file mode to indicate that the file does not exist.
+*/
+
+#define ST_MODE_NOFILE 0171147 /* unlikely to occur */
+
+
+/*
+** These are used in a few cases where we need some special
+** error codes, but where the system doesn't provide something
+** reasonable. They are printed in errstring.
+*/
+
+#ifndef E_PSEUDOBASE
+# define E_PSEUDOBASE 256
+#endif
+
+#define E_SM_OPENTIMEOUT (E_PSEUDOBASE + 0) /* Timeout on file open */
+#define E_SM_NOSLINK (E_PSEUDOBASE + 1) /* Symbolic links not allowed */
+#define E_SM_NOHLINK (E_PSEUDOBASE + 2) /* Hard links not allowed */
+#define E_SM_REGONLY (E_PSEUDOBASE + 3) /* Regular files only */
+#define E_SM_ISEXEC (E_PSEUDOBASE + 4) /* Executable files not allowed */
+#define E_SM_WWDIR (E_PSEUDOBASE + 5) /* World writable directory */
+#define E_SM_GWDIR (E_PSEUDOBASE + 6) /* Group writable directory */
+#define E_SM_FILECHANGE (E_PSEUDOBASE + 7) /* File changed after open */
+#define E_SM_WWFILE (E_PSEUDOBASE + 8) /* World writable file */
+#define E_SM_GWFILE (E_PSEUDOBASE + 9) /* Group writable file */
+#define E_DNSBASE (E_PSEUDOBASE + 20) /* base for DNS h_errno */
+
+/* type of arbitrary pointer */
+#ifndef ARBPTR_T
+# define ARBPTR_T void *
+#endif
+
+#ifndef __P
+# include "cdefs.h"
+#endif
+
+#if HESIOD && !defined(NAMED_BIND)
+# define NAMED_BIND 1 /* not one without the other */
+#endif
+
+#if NAMED_BIND && !defined(__ksr__) && !defined(h_errno)
+extern int h_errno;
+#endif
+
+/*
+** Do some required dependencies
+*/
+
+#if NETINET || NETISO
+# ifndef SMTP
+# define SMTP 1 /* enable user and server SMTP */
+# endif
+# ifndef QUEUE
+# define QUEUE 1 /* enable queueing */
+# endif
+# ifndef DAEMON
+# define DAEMON 1 /* include the daemon (requires IPC & SMTP) */
+# endif
+#endif
+
+
+/*
+** Arrange to use either varargs or stdargs
+*/
+
+# ifdef __STDC__
+
+# include <stdarg.h>
+
+# define VA_LOCAL_DECL va_list ap;
+# define VA_START(f) va_start(ap, f)
+# define VA_END va_end(ap)
+
+# else
+
+# include <varargs.h>
+
+# define VA_LOCAL_DECL va_list ap;
+# define VA_START(f) va_start(ap)
+# define VA_END va_end(ap)
+
+# endif
+
+#ifdef HASUNAME
+# include <sys/utsname.h>
+# ifdef newstr
+# undef newstr
+# endif
+#else /* ! HASUNAME */
+# define NODE_LENGTH 32
+struct utsname
+{
+ char nodename[NODE_LENGTH+1];
+};
+#endif /* HASUNAME */
+
+#if !defined(MAXHOSTNAMELEN) && !defined(_SCO_unix_) && !defined(NonStop_UX_BXX) && !defined(ALTOS_SYSTEM_V)
+# define MAXHOSTNAMELEN 256
+#endif
+
+#if !defined(SIGCHLD) && defined(SIGCLD)
+# define SIGCHLD SIGCLD
+#endif
+
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+#endif
+
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+
+#ifndef STDERR_FILENO
+# define STDERR_FILENO 2
+#endif
+
+#ifndef LOCK_SH
+# define LOCK_SH 0x01 /* shared lock */
+# define LOCK_EX 0x02 /* exclusive lock */
+# define LOCK_NB 0x04 /* non-blocking lock */
+# define LOCK_UN 0x08 /* unlock */
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH (S_IEXEC >> 6)
+#endif
+
+#ifndef S_IXGRP
+# define S_IXGRP (S_IEXEC >> 3)
+#endif
+
+#ifndef S_IXUSR
+# define S_IXUSR (S_IEXEC)
+#endif
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+# define SEEK_CUR 1
+# define SEEK_END 2
+#endif
+
+#ifndef SIG_ERR
+# define SIG_ERR ((void (*)()) -1)
+#endif
+
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(st) (((st) >> 8) & 0377)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(st) (((st) & 0377) == 0)
+#endif
+
+#ifndef SIGFUNC_DEFINED
+typedef void (*sigfunc_t) __P((int));
+#endif
+#ifndef SIGFUNC_RETURN
+# define SIGFUNC_RETURN
+#endif
+#ifndef SIGFUNC_DECL
+# define SIGFUNC_DECL void
+#endif
+
+/* size of syslog buffer */
+#ifndef SYSLOG_BUFSIZE
+# define SYSLOG_BUFSIZE 1024
+#endif
+
+/*
+** Size of prescan buffer.
+** Despite comments in the _sendmail_ book, this probably should
+** not be changed; there are some hard-to-define dependencies.
+*/
+
+# define PSBUFSIZE (MAXNAME + MAXATOM) /* size of prescan buffer */
+
+/* fork routine -- set above using #ifdef _osname_ or in Makefile */
+# ifndef FORK
+# define FORK fork /* function to call to fork mailer */
+# endif
+
+/*
+** Default to using scanf in readcf.
+*/
+
+#ifndef SCANF
+# define SCANF 1
+#endif
+
+/*
+** SVr4 and similar systems use different routines for setjmp/longjmp
+** with signal support
+*/
+
+#if USE_SIGLONGJMP
+# ifdef jmp_buf
+# undef jmp_buf
+# endif
+# define jmp_buf sigjmp_buf
+# ifdef setjmp
+# undef setjmp
+# endif
+# define setjmp(env) sigsetjmp(env, 1)
+# ifdef longjmp
+# undef longjmp
+# endif
+# define longjmp(env, val) siglongjmp(env, val)
+#endif
+
+#if !defined(NGROUPS_MAX) && defined(NGROUPS)
+# define NGROUPS_MAX NGROUPS /* POSIX naming convention */
+#endif
+
+/*
+** If we don't have a system syslog, simulate it.
+*/
+
+#if !LOG
+# define LOG_EMERG 0 /* system is unusable */
+# define LOG_ALERT 1 /* action must be taken immediately */
+# define LOG_CRIT 2 /* critical conditions */
+# define LOG_ERR 3 /* error conditions */
+# define LOG_WARNING 4 /* warning conditions */
+# define LOG_NOTICE 5 /* normal but significant condition */
+# define LOG_INFO 6 /* informational */
+# define LOG_DEBUG 7 /* debug-level messages */
+#endif
diff --git a/contrib/sendmail/src/convtime.c b/contrib/sendmail/src/convtime.c
new file mode 100644
index 0000000..02c287b
--- /dev/null
+++ b/contrib/sendmail/src/convtime.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)convtime.c 8.14 (Berkeley) 5/19/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+** CONVTIME -- convert time
+**
+** Takes a time as an ascii string with a trailing character
+** giving units:
+** s -- seconds
+** m -- minutes
+** h -- hours
+** d -- days (default)
+** w -- weeks
+** For example, "3d12h" is three and a half days.
+**
+** Parameters:
+** p -- pointer to ascii time.
+** units -- default units if none specified.
+**
+** Returns:
+** time in seconds.
+**
+** Side Effects:
+** none.
+*/
+
+time_t
+convtime(p, units)
+ char *p;
+ char units;
+{
+ register time_t t, r;
+ register char c;
+
+ r = 0;
+ while (*p != '\0')
+ {
+ t = 0;
+ while ((c = *p++) != '\0' && isascii(c) && isdigit(c))
+ t = t * 10 + (c - '0');
+ if (c == '\0')
+ {
+ c = units;
+ p--;
+ }
+ else if (strchr("wdhms", c) == NULL)
+ {
+ usrerr("Invalid time unit `%c'", c);
+ c = units;
+ }
+ switch (c)
+ {
+ case 'w': /* weeks */
+ t *= 7;
+
+ case 'd': /* days */
+ default:
+ t *= 24;
+
+ case 'h': /* hours */
+ t *= 60;
+
+ case 'm': /* minutes */
+ t *= 60;
+
+ case 's': /* seconds */
+ break;
+ }
+ r += t;
+ }
+
+ return (r);
+}
+ /*
+** PINTVL -- produce printable version of a time interval
+**
+** Parameters:
+** intvl -- the interval to be converted
+** brief -- if TRUE, print this in an extremely compact form
+** (basically used for logging).
+**
+** Returns:
+** A pointer to a string version of intvl suitable for
+** printing or framing.
+**
+** Side Effects:
+** none.
+**
+** Warning:
+** The string returned is in a static buffer.
+*/
+
+# define PLURAL(n) ((n) == 1 ? "" : "s")
+
+char *
+pintvl(intvl, brief)
+ time_t intvl;
+ bool brief;
+{
+ static char buf[256];
+ register char *p;
+ int wk, dy, hr, mi, se;
+
+ if (intvl == 0 && !brief)
+ return ("zero seconds");
+
+ /* decode the interval into weeks, days, hours, minutes, seconds */
+ se = intvl % 60;
+ intvl /= 60;
+ mi = intvl % 60;
+ intvl /= 60;
+ hr = intvl % 24;
+ intvl /= 24;
+ if (brief)
+ {
+ dy = intvl;
+ wk = 0;
+ }
+ else
+ {
+ dy = intvl % 7;
+ intvl /= 7;
+ wk = intvl;
+ }
+
+ /* now turn it into a sexy form */
+ p = buf;
+ if (brief)
+ {
+ if (dy > 0)
+ {
+ (void) snprintf(p, SPACELEFT(buf, p), "%d+", dy);
+ p += strlen(p);
+ }
+ (void) snprintf(p, SPACELEFT(buf, p), "%02d:%02d:%02d",
+ hr, mi, se);
+ return (buf);
+ }
+
+ /* use the verbose form */
+ if (wk > 0)
+ {
+ (void) snprintf(p, SPACELEFT(buf, p), ", %d week%s", wk, PLURAL(wk));
+ p += strlen(p);
+ }
+ if (dy > 0)
+ {
+ (void) snprintf(p, SPACELEFT(buf, p), ", %d day%s", dy, PLURAL(dy));
+ p += strlen(p);
+ }
+ if (hr > 0)
+ {
+ (void) snprintf(p, SPACELEFT(buf, p), ", %d hour%s", hr, PLURAL(hr));
+ p += strlen(p);
+ }
+ if (mi > 0)
+ {
+ (void) snprintf(p, SPACELEFT(buf, p), ", %d minute%s", mi, PLURAL(mi));
+ p += strlen(p);
+ }
+ if (se > 0)
+ {
+ (void) snprintf(p, SPACELEFT(buf, p), ", %d second%s", se, PLURAL(se));
+ p += strlen(p);
+ }
+
+ return (buf + 2);
+}
diff --git a/contrib/sendmail/src/daemon.c b/contrib/sendmail/src/daemon.c
new file mode 100644
index 0000000..dc0b5b5
--- /dev/null
+++ b/contrib/sendmail/src/daemon.c
@@ -0,0 +1,2073 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#include <errno.h>
+#include "sendmail.h"
+
+#ifndef lint
+#ifdef DAEMON
+static char sccsid[] = "@(#)daemon.c 8.220 (Berkeley) 6/24/98 (with daemon mode)";
+#else
+static char sccsid[] = "@(#)daemon.c 8.220 (Berkeley) 6/24/98 (without daemon mode)";
+#endif
+#endif /* not lint */
+
+#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__)
+# define USE_SOCK_STREAM 1
+#endif
+
+#if DAEMON || defined(USE_SOCK_STREAM)
+# include <arpa/inet.h>
+# if NAMED_BIND
+# include <resolv.h>
+# ifndef NO_DATA
+# define NO_DATA NO_ADDRESS
+# endif
+# endif
+#endif
+
+#if DAEMON
+
+# include <sys/time.h>
+
+# if IP_SRCROUTE
+# include <netinet/in_systm.h>
+# include <netinet/ip.h>
+# include <netinet/ip_var.h>
+# endif
+
+/*
+** DAEMON.C -- routines to use when running as a daemon.
+**
+** This entire file is highly dependent on the 4.2 BSD
+** interprocess communication primitives. No attempt has
+** been made to make this file portable to Version 7,
+** Version 6, MPX files, etc. If you should try such a
+** thing yourself, I recommend chucking the entire file
+** and starting from scratch. Basic semantics are:
+**
+** getrequests(e)
+** Opens a port and initiates a connection.
+** Returns in a child. Must set InChannel and
+** OutChannel appropriately.
+** clrdaemon()
+** Close any open files associated with getting
+** the connection; this is used when running the queue,
+** etc., to avoid having extra file descriptors during
+** the queue run and to avoid confusing the network
+** code (if it cares).
+** makeconnection(host, port, outfile, infile, e)
+** Make a connection to the named host on the given
+** port. Set *outfile and *infile to the files
+** appropriate for communication. Returns zero on
+** success, else an exit status describing the
+** error.
+** host_map_lookup(map, hbuf, avp, pstat)
+** Convert the entry in hbuf into a canonical form.
+*/
+ /*
+** GETREQUESTS -- open mail IPC port and get requests.
+**
+** Parameters:
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Waits until some interesting activity occurs. When
+** it does, a child is created to process it, and the
+** parent waits for completion. Return from this
+** routine is always in the child. The file pointers
+** "InChannel" and "OutChannel" should be set to point
+** to the communication channel.
+*/
+
+int DaemonSocket = -1; /* fd describing socket */
+SOCKADDR DaemonAddr; /* socket for incoming */
+int ListenQueueSize = 10; /* size of listen queue */
+int TcpRcvBufferSize = 0; /* size of TCP receive buffer */
+int TcpSndBufferSize = 0; /* size of TCP send buffer */
+
+void
+getrequests(e)
+ ENVELOPE *e;
+{
+ int t;
+ bool refusingconnections = TRUE;
+ FILE *pidf;
+ int socksize;
+ u_short port;
+#if XDEBUG
+ bool j_has_dot;
+#endif
+ extern void reapchild __P((int));
+ extern int opendaemonsocket __P((bool));
+
+ /*
+ ** Set up the address for the mailer.
+ */
+
+ switch (DaemonAddr.sa.sa_family)
+ {
+ case AF_UNSPEC:
+ DaemonAddr.sa.sa_family = AF_INET;
+ /* fall through ... */
+
+ case AF_INET:
+ if (DaemonAddr.sin.sin_addr.s_addr == 0)
+ DaemonAddr.sin.sin_addr.s_addr = INADDR_ANY;
+ port = DaemonAddr.sin.sin_port;
+ break;
+
+ default:
+ /* unknown protocol */
+ port = 0;
+ break;
+ }
+ if (port == 0)
+ {
+ register struct servent *sp;
+
+ sp = getservbyname("smtp", "tcp");
+ if (sp == NULL)
+ {
+ syserr("554 service \"smtp\" unknown");
+ port = htons(25);
+ }
+ else
+ port = sp->s_port;
+ }
+
+ switch (DaemonAddr.sa.sa_family)
+ {
+ case AF_INET:
+ DaemonAddr.sin.sin_port = port;
+ break;
+
+ default:
+ /* unknown protocol */
+ break;
+ }
+
+ /*
+ ** Try to actually open the connection.
+ */
+
+ if (tTd(15, 1))
+ printf("getrequests: port 0x%x\n", port);
+
+ /* get a socket for the SMTP connection */
+ socksize = opendaemonsocket(TRUE);
+
+ (void) setsignal(SIGCHLD, reapchild);
+
+ /* write the pid to the log file for posterity */
+ pidf = safefopen(PidFile, O_WRONLY|O_TRUNC, 0644,
+ SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT);
+ if (pidf == NULL)
+ {
+ sm_syslog(LOG_ERR, NOQID, "unable to write %s", PidFile);
+ }
+ else
+ {
+ extern char *CommandLineArgs;
+
+ /* write the process id on line 1 */
+ fprintf(pidf, "%ld\n", (long) getpid());
+
+ /* line 2 contains all command line flags */
+ fprintf(pidf, "%s\n", CommandLineArgs);
+
+ /* flush and close */
+ fclose(pidf);
+ }
+
+#if XDEBUG
+ {
+ char jbuf[MAXHOSTNAMELEN];
+
+ expand("\201j", jbuf, sizeof jbuf, e);
+ j_has_dot = strchr(jbuf, '.') != NULL;
+ }
+#endif
+
+ if (tTd(15, 1))
+ printf("getrequests: %d\n", DaemonSocket);
+
+ for (;;)
+ {
+ register pid_t pid;
+ auto SOCKADDR_LEN_T lotherend;
+ int savederrno;
+ int pipefd[2];
+ extern bool refuseconnections __P((int));
+
+ /* see if we are rejecting connections */
+ (void) blocksignal(SIGALRM);
+ if (refuseconnections(ntohs(port)))
+ {
+ if (DaemonSocket >= 0)
+ {
+ /* close socket so peer will fail quickly */
+ (void) close(DaemonSocket);
+ DaemonSocket = -1;
+ }
+ refusingconnections = TRUE;
+ sleep(15);
+ continue;
+ }
+
+ /* arrange to (re)open the socket if necessary */
+ if (refusingconnections)
+ {
+ (void) opendaemonsocket(FALSE);
+ refusingconnections = FALSE;
+ }
+
+#if XDEBUG
+ /* check for disaster */
+ {
+ char jbuf[MAXHOSTNAMELEN];
+ extern void dumpstate __P((char *));
+
+ expand("\201j", jbuf, sizeof jbuf, e);
+ if (!wordinclass(jbuf, 'w'))
+ {
+ dumpstate("daemon lost $j");
+ sm_syslog(LOG_ALERT, NOQID,
+ "daemon process doesn't have $j in $=w; see syslog");
+ abort();
+ }
+ else if (j_has_dot && strchr(jbuf, '.') == NULL)
+ {
+ dumpstate("daemon $j lost dot");
+ sm_syslog(LOG_ALERT, NOQID,
+ "daemon process $j lost dot; see syslog");
+ abort();
+ }
+ }
+#endif
+
+ /* wait for a connection */
+ setproctitle("accepting connections on port %d",
+ ntohs(port));
+#if 0
+ /*
+ ** Andrew Sun <asun@ieps-sun.ml.com> claims that this will
+ ** fix the SVr4 problem. But it seems to have gone away,
+ ** so is it worth doing this?
+ */
+
+ if (SetNonBlocking(DaemonSocket, FALSE) < 0)
+ log an error here;
+#endif
+ (void) releasesignal(SIGALRM);
+ for (;;)
+ {
+ fd_set readfds;
+ struct timeval timeout;
+
+ FD_ZERO(&readfds);
+ FD_SET(DaemonSocket, &readfds);
+ timeout.tv_sec = 60;
+ timeout.tv_usec = 0;
+
+ t = select(DaemonSocket + 1, FDSET_CAST &readfds,
+ NULL, NULL, &timeout);
+ if (DoQueueRun)
+ (void) runqueue(TRUE, FALSE);
+ if (t <= 0 || !FD_ISSET(DaemonSocket, &readfds))
+ continue;
+
+ errno = 0;
+ lotherend = socksize;
+ t = accept(DaemonSocket,
+ (struct sockaddr *)&RealHostAddr, &lotherend);
+ if (t >= 0 || errno != EINTR)
+ break;
+ }
+ savederrno = errno;
+ (void) blocksignal(SIGALRM);
+ if (t < 0)
+ {
+ errno = savederrno;
+ syserr("getrequests: accept");
+
+ /* arrange to re-open the socket next time around */
+ (void) close(DaemonSocket);
+ DaemonSocket = -1;
+ refusingconnections = TRUE;
+ sleep(5);
+ continue;
+ }
+
+ /*
+ ** Create a subprocess to process the mail.
+ */
+
+ if (tTd(15, 2))
+ printf("getrequests: forking (fd = %d)\n", t);
+
+ /*
+ ** Create a pipe to keep the child from writing to the
+ ** socket until after the parent has closed it. Otherwise
+ ** the parent may hang if the child has closed it first.
+ */
+
+ if (pipe(pipefd) < 0)
+ pipefd[0] = pipefd[1] = -1;
+
+ blocksignal(SIGCHLD);
+ pid = fork();
+ if (pid < 0)
+ {
+ syserr("daemon: cannot fork");
+ if (pipefd[0] != -1)
+ {
+ (void) close(pipefd[0]);
+ (void) close(pipefd[1]);
+ }
+ (void) releasesignal(SIGCHLD);
+ sleep(10);
+ (void) close(t);
+ continue;
+ }
+
+ if (pid == 0)
+ {
+ char *p;
+ extern SIGFUNC_DECL intsig __P((int));
+ FILE *inchannel, *outchannel;
+
+ /*
+ ** CHILD -- return to caller.
+ ** Collect verified idea of sending host.
+ ** Verify calling user id if possible here.
+ */
+
+ (void) releasesignal(SIGALRM);
+ (void) releasesignal(SIGCHLD);
+ (void) setsignal(SIGCHLD, SIG_DFL);
+ (void) setsignal(SIGHUP, intsig);
+ (void) close(DaemonSocket);
+ proc_list_clear();
+
+ /* don't schedule queue runs if we are told to ETRN */
+ QueueIntvl = 0;
+
+ setproctitle("startup with %s",
+ anynet_ntoa(&RealHostAddr));
+
+ if (pipefd[0] != -1)
+ {
+ auto char c;
+
+ /*
+ ** Wait for the parent to close the write end
+ ** of the pipe, which we will see as an EOF.
+ ** This guarantees that we won't write to the
+ ** socket until after the parent has closed
+ ** the pipe.
+ */
+
+ /* close the write end of the pipe */
+ (void) close(pipefd[1]);
+
+ /* we shouldn't be interrupted, but ... */
+ while (read(pipefd[0], &c, 1) < 0 &&
+ errno == EINTR)
+ continue;
+ (void) close(pipefd[0]);
+ }
+
+ /* determine host name */
+ p = hostnamebyanyaddr(&RealHostAddr);
+ if (strlen(p) > (SIZE_T) MAXNAME)
+ p[MAXNAME] = '\0';
+ RealHostName = newstr(p);
+ setproctitle("startup with %s", p);
+
+ if ((inchannel = fdopen(t, "r")) == NULL ||
+ (t = dup(t)) < 0 ||
+ (outchannel = fdopen(t, "w")) == NULL)
+ {
+ syserr("cannot open SMTP server channel, fd=%d", t);
+ exit(EX_OK);
+ }
+
+ InChannel = inchannel;
+ OutChannel = outchannel;
+ DisConnected = FALSE;
+
+ /* open maps for check_relay ruleset */
+ initmaps(FALSE, e);
+
+#ifdef XLA
+ if (!xla_host_ok(RealHostName))
+ {
+ message("421 Too many SMTP sessions for this host");
+ exit(EX_OK);
+ }
+#endif
+
+ break;
+ }
+
+ /* parent -- keep track of children */
+ proc_list_add(pid);
+ (void) releasesignal(SIGCHLD);
+
+ /* close the read end of the synchronization pipe */
+ if (pipefd[0] != -1)
+ (void) close(pipefd[0]);
+
+ /* close the port so that others will hang (for a while) */
+ (void) close(t);
+
+ /* release the child by closing the read end of the sync pipe */
+ if (pipefd[1] != -1)
+ (void) close(pipefd[1]);
+ }
+ if (tTd(15, 2))
+ printf("getreq: returning\n");
+ return;
+}
+ /*
+** OPENDAEMONSOCKET -- open the SMTP socket
+**
+** Deals with setting all appropriate options. DaemonAddr must
+** be set up in advance.
+**
+** Parameters:
+** firsttime -- set if this is the initial open.
+**
+** Returns:
+** Size in bytes of the daemon socket addr.
+**
+** Side Effects:
+** Leaves DaemonSocket set to the open socket.
+** Exits if the socket cannot be created.
+*/
+
+#define MAXOPENTRIES 10 /* maximum number of tries to open connection */
+
+int
+opendaemonsocket(firsttime)
+ bool firsttime;
+{
+ int on = 1;
+ int socksize = 0;
+ int ntries = 0;
+ int saveerrno;
+
+ if (tTd(15, 2))
+ printf("opendaemonsocket()\n");
+
+ do
+ {
+ if (ntries > 0)
+ sleep(5);
+ if (firsttime || DaemonSocket < 0)
+ {
+ DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0);
+ if (DaemonSocket < 0)
+ {
+ saveerrno = errno;
+ syserr("opendaemonsocket: can't create server SMTP socket");
+ severe:
+ if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, NOQID,
+ "problem creating SMTP socket");
+ DaemonSocket = -1;
+ continue;
+ }
+
+ /* turn on network debugging? */
+ if (tTd(15, 101))
+ (void) setsockopt(DaemonSocket, SOL_SOCKET,
+ SO_DEBUG, (char *)&on,
+ sizeof on);
+
+ (void) setsockopt(DaemonSocket, SOL_SOCKET,
+ SO_REUSEADDR, (char *)&on, sizeof on);
+ (void) setsockopt(DaemonSocket, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof on);
+
+#ifdef SO_RCVBUF
+ if (TcpRcvBufferSize > 0)
+ {
+ if (setsockopt(DaemonSocket, SOL_SOCKET,
+ SO_RCVBUF,
+ (char *) &TcpRcvBufferSize,
+ sizeof(TcpRcvBufferSize)) < 0)
+ syserr("opendaemonsocket: setsockopt(SO_RCVBUF)");
+ }
+#endif
+
+ switch (DaemonAddr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ socksize = sizeof DaemonAddr.sin;
+ break;
+# endif
+
+# if NETISO
+ case AF_ISO:
+ socksize = sizeof DaemonAddr.siso;
+ break;
+# endif
+
+ default:
+ socksize = sizeof DaemonAddr;
+ break;
+ }
+
+ if (bind(DaemonSocket, &DaemonAddr.sa, socksize) < 0)
+ {
+ /* probably another daemon already */
+ saveerrno = errno;
+ syserr("opendaemonsocket: cannot bind");
+ (void) close(DaemonSocket);
+ goto severe;
+ }
+ }
+ if (!firsttime && listen(DaemonSocket, ListenQueueSize) < 0)
+ {
+ saveerrno = errno;
+ syserr("opendaemonsocket: cannot listen");
+ (void) close(DaemonSocket);
+ goto severe;
+ }
+ return socksize;
+ } while (ntries++ < MAXOPENTRIES && transienterror(saveerrno));
+ syserr("!opendaemonsocket: server SMTP socket wedged: exiting");
+ finis();
+ return -1; /* avoid compiler warning on IRIX */
+}
+ /*
+** CLRDAEMON -- reset the daemon connection
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** releases any resources used by the passive daemon.
+*/
+
+void
+clrdaemon()
+{
+ if (DaemonSocket >= 0)
+ (void) close(DaemonSocket);
+ DaemonSocket = -1;
+}
+ /*
+** SETDAEMONOPTIONS -- set options for running the daemon
+**
+** Parameters:
+** p -- the options line.
+**
+** Returns:
+** none.
+*/
+
+void
+setdaemonoptions(p)
+ register char *p;
+{
+ if (DaemonAddr.sa.sa_family == AF_UNSPEC)
+ DaemonAddr.sa.sa_family = AF_INET;
+
+ while (p != NULL)
+ {
+ register char *f;
+ register char *v;
+
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ f = p;
+ p = strchr(p, ',');
+ if (p != NULL)
+ *p++ = '\0';
+ v = strchr(f, '=');
+ if (v == NULL)
+ continue;
+ while (isascii(*++v) && isspace(*v))
+ continue;
+ if (isascii(*f) && islower(*f))
+ *f = toupper(*f);
+
+ switch (*f)
+ {
+ case 'F': /* address family */
+ if (isascii(*v) && isdigit(*v))
+ DaemonAddr.sa.sa_family = atoi(v);
+#if NETINET
+ else if (strcasecmp(v, "inet") == 0)
+ DaemonAddr.sa.sa_family = AF_INET;
+#endif
+#if NETISO
+ else if (strcasecmp(v, "iso") == 0)
+ DaemonAddr.sa.sa_family = AF_ISO;
+#endif
+#if NETNS
+ else if (strcasecmp(v, "ns") == 0)
+ DaemonAddr.sa.sa_family = AF_NS;
+#endif
+#if NETX25
+ else if (strcasecmp(v, "x.25") == 0)
+ DaemonAddr.sa.sa_family = AF_CCITT;
+#endif
+ else
+ syserr("554 Unknown address family %s in Family=option", v);
+ break;
+
+ case 'A': /* address */
+ switch (DaemonAddr.sa.sa_family)
+ {
+#if NETINET
+ case AF_INET:
+ if (isascii(*v) && isdigit(*v))
+ DaemonAddr.sin.sin_addr.s_addr = inet_addr(v);
+ else
+ {
+ register struct hostent *hp;
+
+ hp = sm_gethostbyname(v);
+ if (hp == NULL)
+ syserr("554 host \"%s\" unknown", v);
+ else
+ bcopy(hp->h_addr, &DaemonAddr.sin.sin_addr, INADDRSZ);
+ }
+ break;
+#endif
+
+ default:
+ syserr("554 Address= option unsupported for family %d",
+ DaemonAddr.sa.sa_family);
+ break;
+ }
+ break;
+
+ case 'P': /* port */
+ switch (DaemonAddr.sa.sa_family)
+ {
+#if NETISO
+ short port;
+#endif
+
+#if NETINET
+ case AF_INET:
+ if (isascii(*v) && isdigit(*v))
+ DaemonAddr.sin.sin_port = htons(atoi(v));
+ else
+ {
+ register struct servent *sp;
+
+ sp = getservbyname(v, "tcp");
+ if (sp == NULL)
+ syserr("554 service \"%s\" unknown", v);
+ else
+ DaemonAddr.sin.sin_port = sp->s_port;
+ }
+ break;
+#endif
+
+#if NETISO
+ case AF_ISO:
+ /* assume two byte transport selector */
+ if (isascii(*v) && isdigit(*v))
+ port = htons(atoi(v));
+ else
+ {
+ register struct servent *sp;
+
+ sp = getservbyname(v, "tcp");
+ if (sp == NULL)
+ syserr("554 service \"%s\" unknown", v);
+ else
+ port = sp->s_port;
+ }
+ bcopy((char *) &port, TSEL(&DaemonAddr.siso), 2);
+ break;
+#endif
+
+ default:
+ syserr("554 Port= option unsupported for family %d",
+ DaemonAddr.sa.sa_family);
+ break;
+ }
+ break;
+
+ case 'L': /* listen queue size */
+ ListenQueueSize = atoi(v);
+ break;
+
+ case 'S': /* send buffer size */
+ TcpSndBufferSize = atoi(v);
+ break;
+
+ case 'R': /* receive buffer size */
+ TcpRcvBufferSize = atoi(v);
+ break;
+
+ default:
+ syserr("554 DaemonPortOptions parameter \"%s\" unknown", f);
+ }
+ }
+}
+ /*
+** MAKECONNECTION -- make a connection to an SMTP socket on another machine.
+**
+** Parameters:
+** host -- the name of the host.
+** port -- the port number to connect to.
+** mci -- a pointer to the mail connection information
+** structure to be filled in.
+** e -- the current envelope.
+**
+** Returns:
+** An exit code telling whether the connection could be
+** made and if not why not.
+**
+** Side Effects:
+** none.
+*/
+
+static jmp_buf CtxConnectTimeout;
+
+static void
+connecttimeout()
+{
+ errno = ETIMEDOUT;
+ longjmp(CtxConnectTimeout, 1);
+}
+
+SOCKADDR CurHostAddr; /* address of current host */
+
+int
+makeconnection(host, port, mci, e)
+ char *host;
+ u_short port;
+ register MCI *mci;
+ ENVELOPE *e;
+{
+ register volatile int addrno = 0;
+ register volatile int s;
+ register struct hostent *volatile hp = (struct hostent *)NULL;
+ SOCKADDR addr;
+ int sav_errno;
+ volatile int addrlen;
+ volatile bool firstconnect;
+ EVENT *volatile ev = NULL;
+
+ /*
+ ** Set up the address for the mailer.
+ ** Accept "[a.b.c.d]" syntax for host name.
+ */
+
+#if NAMED_BIND
+ h_errno = 0;
+#endif
+ errno = 0;
+ bzero(&CurHostAddr, sizeof CurHostAddr);
+ SmtpPhase = mci->mci_phase = "initial connection";
+ CurHostName = host;
+
+ if (host[0] == '[')
+ {
+#if NETINET
+ unsigned long hid = INADDR_NONE;
+#endif
+ register char *p = strchr(host, ']');
+
+ if (p != NULL)
+ {
+ *p = '\0';
+#if NETINET
+ hid = inet_addr(&host[1]);
+ if (hid == INADDR_NONE)
+#endif
+ {
+ /* try it as a host name (avoid MX lookup) */
+ hp = sm_gethostbyname(&host[1]);
+ if (hp == NULL && p[-1] == '.')
+ {
+#if NAMED_BIND
+ int oldopts = _res.options;
+
+ _res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
+#endif
+ p[-1] = '\0';
+ hp = sm_gethostbyname(&host[1]);
+ p[-1] = '.';
+#if NAMED_BIND
+ _res.options = oldopts;
+#endif
+ }
+ *p = ']';
+ goto gothostent;
+ }
+ *p = ']';
+ }
+ if (p == NULL)
+ {
+ extern char MsgBuf[];
+
+ usrerr("553 Invalid numeric domain spec \"%s\"", host);
+ mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf);
+ return EX_NOHOST;
+ }
+#if NETINET
+ addr.sin.sin_family = AF_INET; /*XXX*/
+ addr.sin.sin_addr.s_addr = hid;
+#endif
+ }
+ else
+ {
+ /* contortion to get around SGI cc complaints */
+ {
+ register char *p = &host[strlen(host) - 1];
+
+ hp = sm_gethostbyname(host);
+ if (hp == NULL && *p == '.')
+ {
+#if NAMED_BIND
+ int oldopts = _res.options;
+
+ _res.options &= ~(RES_DEFNAMES|RES_DNSRCH);
+#endif
+ *p = '\0';
+ hp = sm_gethostbyname(host);
+ *p = '.';
+#if NAMED_BIND
+ _res.options = oldopts;
+#endif
+ }
+ }
+gothostent:
+ if (hp == NULL)
+ {
+#if NAMED_BIND
+ /* check for name server timeouts */
+ if (errno == ETIMEDOUT || h_errno == TRY_AGAIN ||
+ (errno == ECONNREFUSED && UseNameServer))
+ {
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.3", NULL);
+ return EX_TEMPFAIL;
+ }
+#endif
+ mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
+ return (EX_NOHOST);
+ }
+ addr.sa.sa_family = hp->h_addrtype;
+ switch (hp->h_addrtype)
+ {
+#if NETINET
+ case AF_INET:
+ bcopy(hp->h_addr,
+ &addr.sin.sin_addr,
+ INADDRSZ);
+ break;
+#endif
+
+ default:
+ if (hp->h_length > sizeof addr.sa.sa_data)
+ {
+ syserr("makeconnection: long sa_data: family %d len %d",
+ hp->h_addrtype, hp->h_length);
+ mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
+ return EX_NOHOST;
+ }
+ bcopy(hp->h_addr,
+ addr.sa.sa_data,
+ hp->h_length);
+ break;
+ }
+ addrno = 1;
+ }
+
+ /*
+ ** Determine the port number.
+ */
+
+ if (port == 0)
+ {
+ register struct servent *sp = getservbyname("smtp", "tcp");
+
+ if (sp == NULL)
+ {
+ if (LogLevel > 2)
+ sm_syslog(LOG_ERR, NOQID,
+ "makeconnection: service \"smtp\" unknown");
+ port = htons(25);
+ }
+ else
+ port = sp->s_port;
+ }
+
+ switch (addr.sa.sa_family)
+ {
+#if NETINET
+ case AF_INET:
+ addr.sin.sin_port = port;
+ addrlen = sizeof (struct sockaddr_in);
+ break;
+#endif
+
+#if NETISO
+ case AF_ISO:
+ /* assume two byte transport selector */
+ bcopy((char *) &port, TSEL((struct sockaddr_iso *) &addr), 2);
+ addrlen = sizeof (struct sockaddr_iso);
+ break;
+#endif
+
+ default:
+ syserr("Can't connect to address family %d", addr.sa.sa_family);
+ mci_setstat(mci, EX_NOHOST, "5.1.2", NULL);
+ return (EX_NOHOST);
+ }
+
+ /*
+ ** Try to actually open the connection.
+ */
+
+#ifdef XLA
+ /* if too many connections, don't bother trying */
+ if (!xla_noqueue_ok(host))
+ return EX_TEMPFAIL;
+#endif
+
+ firstconnect = TRUE;
+ for (;;)
+ {
+ if (tTd(16, 1))
+ printf("makeconnection (%s [%s])\n",
+ host, anynet_ntoa(&addr));
+
+ /* save for logging */
+ CurHostAddr = addr;
+
+ if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags))
+ {
+ int rport = IPPORT_RESERVED - 1;
+
+ s = rresvport(&rport);
+ }
+ else
+ {
+ s = socket(addr.sa.sa_family, SOCK_STREAM, 0);
+ }
+ if (s < 0)
+ {
+ sav_errno = errno;
+ syserr("makeconnection: cannot create socket");
+#ifdef XLA
+ xla_host_end(host);
+#endif
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
+ return EX_TEMPFAIL;
+ }
+
+#ifdef SO_SNDBUF
+ if (TcpSndBufferSize > 0)
+ {
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
+ (char *) &TcpSndBufferSize,
+ sizeof(TcpSndBufferSize)) < 0)
+ syserr("makeconnection: setsockopt(SO_SNDBUF)");
+ }
+#endif
+
+ if (tTd(16, 1))
+ printf("makeconnection: fd=%d\n", s);
+
+ /* turn on network debugging? */
+ if (tTd(16, 101))
+ {
+ int on = 1;
+ (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof on);
+ }
+ if (e->e_xfp != NULL)
+ (void) fflush(e->e_xfp); /* for debugging */
+ errno = 0; /* for debugging */
+
+ /*
+ ** Linux seems to hang in connect for 90 minutes (!!!).
+ ** Time out the connect to avoid this problem.
+ */
+
+ if (setjmp(CtxConnectTimeout) == 0)
+ {
+ int i;
+
+ if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0)
+ ev = setevent(TimeOuts.to_iconnect, connecttimeout, 0);
+ else if (TimeOuts.to_connect != 0)
+ ev = setevent(TimeOuts.to_connect, connecttimeout, 0);
+ else
+ ev = NULL;
+
+#if _FFR_CONNECTONLYTO_OPTION
+ /* for testing */
+ if (ConnectOnlyTo != 0)
+ addr.sin.sin_addr.s_addr = ConnectOnlyTo;
+#endif
+ i = connect(s, (struct sockaddr *) &addr, addrlen);
+ sav_errno = errno;
+ if (ev != NULL)
+ clrevent(ev);
+ if (i >= 0)
+ break;
+ }
+ else
+ sav_errno = errno;
+
+ /* if running demand-dialed connection, try again */
+ if (DialDelay > 0 && firstconnect)
+ {
+ if (tTd(16, 1))
+ printf("Connect failed (%s); trying again...\n",
+ errstring(sav_errno));
+ firstconnect = FALSE;
+ sleep(DialDelay);
+ continue;
+ }
+
+ /* couldn't connect.... figure out why */
+ (void) close(s);
+
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, e->e_id,
+ "makeconnection (%s [%s]) failed: %s",
+ host, anynet_ntoa(&addr),
+ errstring(sav_errno));
+
+ if (hp != NULL && hp->h_addr_list[addrno] != NULL)
+ {
+ if (tTd(16, 1))
+ printf("Connect failed (%s); trying new address....\n",
+ errstring(sav_errno));
+ switch (addr.sa.sa_family)
+ {
+#if NETINET
+ case AF_INET:
+ bcopy(hp->h_addr_list[addrno++],
+ &addr.sin.sin_addr,
+ INADDRSZ);
+ break;
+#endif
+
+ default:
+ bcopy(hp->h_addr_list[addrno++],
+ addr.sa.sa_data,
+ hp->h_length);
+ break;
+ }
+ continue;
+ }
+
+ /* couldn't open connection */
+#ifdef XLA
+ xla_host_end(host);
+#endif
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL);
+ return EX_TEMPFAIL;
+ }
+
+ /* connection ok, put it into canonical form */
+ if ((mci->mci_out = fdopen(s, "w")) == NULL ||
+ (s = dup(s)) < 0 ||
+ (mci->mci_in = fdopen(s, "r")) == NULL)
+ {
+ syserr("cannot open SMTP client channel, fd=%d", s);
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
+ return EX_TEMPFAIL;
+ }
+
+ mci_setstat(mci, EX_OK, NULL, NULL);
+ return (EX_OK);
+}
+ /*
+** MYHOSTNAME -- return the name of this host.
+**
+** Parameters:
+** hostbuf -- a place to return the name of this host.
+** size -- the size of hostbuf.
+**
+** Returns:
+** A list of aliases for this host.
+**
+** Side Effects:
+** Adds numeric codes to $=w.
+*/
+
+struct hostent *
+myhostname(hostbuf, size)
+ char hostbuf[];
+ int size;
+{
+ register struct hostent *hp;
+
+ if (gethostname(hostbuf, size) < 0)
+ {
+ (void) strcpy(hostbuf, "localhost");
+ }
+ hp = sm_gethostbyname(hostbuf);
+ if (hp == NULL)
+ return NULL;
+ if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL)
+ {
+ (void) strncpy(hostbuf, hp->h_name, size - 1);
+ hostbuf[size - 1] = '\0';
+ }
+
+ /*
+ ** If there is still no dot in the name, try looking for a
+ ** dotted alias.
+ */
+
+ if (strchr(hostbuf, '.') == NULL)
+ {
+ char **ha;
+
+ for (ha = hp->h_aliases; *ha != NULL; ha++)
+ {
+ if (strchr(*ha, '.') != NULL)
+ {
+ (void) strncpy(hostbuf, *ha, size - 1);
+ hostbuf[size - 1] = '\0';
+ break;
+ }
+ }
+ }
+
+ /*
+ ** If _still_ no dot, wait for a while and try again -- it is
+ ** possible that some service is starting up. This can result
+ ** in excessive delays if the system is badly configured, but
+ ** there really isn't a way around that, particularly given that
+ ** the config file hasn't been read at this point.
+ ** All in all, a bit of a mess.
+ */
+
+ if (strchr(hostbuf, '.') == NULL &&
+ !getcanonname(hostbuf, size, TRUE))
+ {
+ sm_syslog(LOG_CRIT, NOQID,
+ "My unqualified host name (%s) unknown; sleeping for retry",
+ hostbuf);
+ message("My unqualified host name (%s) unknown; sleeping for retry",
+ hostbuf);
+ sleep(60);
+ if (!getcanonname(hostbuf, size, TRUE))
+ {
+ sm_syslog(LOG_ALERT, NOQID,
+ "unable to qualify my own domain name (%s) -- using short name",
+ hostbuf);
+ message("WARNING: unable to qualify my own domain name (%s) -- using short name",
+ hostbuf);
+ }
+ }
+ return (hp);
+}
+ /*
+** ADDRCMP -- compare two host addresses
+**
+** Parameters:
+** hp -- hostent structure for the first address
+** ha -- actual first address
+** sa -- second address
+**
+** Returns:
+** 0 -- if ha and sa match
+** else -- they don't match
+*/
+
+int
+addrcmp(hp, ha, sa)
+ struct hostent *hp;
+ char *ha;
+ SOCKADDR *sa;
+{
+ switch (sa->sa.sa_family)
+ {
+ case AF_INET:
+ if (hp->h_addrtype == AF_INET)
+ return bcmp(ha, (char *) &sa->sin.sin_addr, hp->h_length);
+ break;
+
+ }
+ return -1;
+}
+ /*
+** GETAUTHINFO -- get the real host name asociated with a file descriptor
+**
+** Uses RFC1413 protocol to try to get info from the other end.
+**
+** Parameters:
+** fd -- the descriptor
+** may_be_forged -- an outage that is set to TRUE if the
+** forward lookup of RealHostName does not match
+** RealHostAddr; set to FALSE if they do match.
+**
+** Returns:
+** The user@host information associated with this descriptor.
+*/
+
+static jmp_buf CtxAuthTimeout;
+
+static void
+authtimeout()
+{
+ longjmp(CtxAuthTimeout, 1);
+}
+
+char *
+getauthinfo(fd, may_be_forged)
+ int fd;
+ bool *may_be_forged;
+{
+ SOCKADDR_LEN_T falen;
+ register char *volatile p = NULL;
+ SOCKADDR la;
+ SOCKADDR_LEN_T lalen;
+ register struct servent *sp;
+ volatile int s;
+ int i = 0;
+ EVENT *ev;
+ int nleft;
+ struct hostent *hp;
+ char *ostype = NULL;
+ char **ha;
+ char ibuf[MAXNAME + 1];
+ static char hbuf[MAXNAME * 2 + 11];
+
+ *may_be_forged = FALSE;
+ falen = sizeof RealHostAddr;
+ if (isatty(fd) || (i = getpeername(fd, &RealHostAddr.sa, &falen)) < 0 ||
+ falen <= 0 || RealHostAddr.sa.sa_family == 0)
+ {
+ if (i < 0 && errno != ENOTSOCK)
+ return NULL;
+ (void) snprintf(hbuf, sizeof hbuf, "%s@localhost",
+ RealUserName);
+ if (tTd(9, 1))
+ printf("getauthinfo: %s\n", hbuf);
+ return hbuf;
+ }
+
+ if (RealHostName == NULL)
+ {
+ /* translate that to a host name */
+ RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
+ if (strlen(RealHostName) > MAXNAME)
+ RealHostName[MAXNAME - 1] = '\0';
+ }
+
+ /* cross check RealHostName with forward DNS lookup */
+ if (anynet_ntoa(&RealHostAddr)[0] == '[' ||
+ RealHostName[0] == '[')
+ {
+ /*
+ ** address is not a socket or have an
+ ** IP address with no forward lookup
+ */
+ *may_be_forged = FALSE;
+ }
+ else
+ {
+ /* try to match the reverse against the forward lookup */
+ hp = sm_gethostbyname(RealHostName);
+
+ if (hp == NULL)
+ *may_be_forged = TRUE;
+ else
+ {
+ for (ha = hp->h_addr_list; *ha != NULL; ha++)
+ if (addrcmp(hp, *ha, &RealHostAddr) == 0)
+ break;
+ *may_be_forged = *ha == NULL;
+ }
+ }
+
+ if (TimeOuts.to_ident == 0)
+ goto noident;
+
+ lalen = sizeof la;
+ if (RealHostAddr.sa.sa_family != AF_INET ||
+ getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 ||
+ la.sa.sa_family != AF_INET)
+ {
+ /* no ident info */
+ goto noident;
+ }
+
+ /* create ident query */
+ (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n",
+ ntohs(RealHostAddr.sin.sin_port), ntohs(la.sin.sin_port));
+
+ /* create local address */
+ la.sin.sin_port = 0;
+
+ /* create foreign address */
+ sp = getservbyname("auth", "tcp");
+ if (sp != NULL)
+ RealHostAddr.sin.sin_port = sp->s_port;
+ else
+ RealHostAddr.sin.sin_port = htons(113);
+
+ s = -1;
+ if (setjmp(CtxAuthTimeout) != 0)
+ {
+ if (s >= 0)
+ (void) close(s);
+ goto noident;
+ }
+
+ /* put a timeout around the whole thing */
+ ev = setevent(TimeOuts.to_ident, authtimeout, 0);
+
+ /* connect to foreign IDENT server using same address as SMTP socket */
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ {
+ clrevent(ev);
+ goto noident;
+ }
+ if (bind(s, &la.sa, sizeof la.sin) < 0 ||
+ connect(s, &RealHostAddr.sa, sizeof RealHostAddr.sin) < 0)
+ {
+ goto closeident;
+ }
+
+ if (tTd(9, 10))
+ printf("getauthinfo: sent %s", ibuf);
+
+ /* send query */
+ if (write(s, ibuf, strlen(ibuf)) < 0)
+ goto closeident;
+
+ /* get result */
+ p = &ibuf[0];
+ nleft = sizeof ibuf - 1;
+ while ((i = read(s, p, nleft)) > 0)
+ {
+ p += i;
+ nleft -= i;
+ *p = '\0';
+ if (strchr(ibuf, '\n') != NULL)
+ break;
+ }
+ (void) close(s);
+ clrevent(ev);
+ if (i < 0 || p == &ibuf[0])
+ goto noident;
+
+ if (*--p == '\n' && *--p == '\r')
+ p--;
+ *++p = '\0';
+
+ if (tTd(9, 3))
+ printf("getauthinfo: got %s\n", ibuf);
+
+ /* parse result */
+ p = strchr(ibuf, ':');
+ if (p == NULL)
+ {
+ /* malformed response */
+ goto noident;
+ }
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ if (strncasecmp(p, "userid", 6) != 0)
+ {
+ /* presumably an error string */
+ goto noident;
+ }
+ p += 6;
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p++ != ':')
+ {
+ /* either useridxx or malformed response */
+ goto noident;
+ }
+
+ /* p now points to the OSTYPE field */
+ while (isascii(*p) && isspace(*p))
+ p++;
+ ostype = p;
+ p = strchr(p, ':');
+ if (p == NULL)
+ {
+ /* malformed response */
+ goto noident;
+ }
+ else
+ {
+ char *charset;
+
+ *p = '\0';
+ charset = strchr(ostype, ',');
+ if (charset != NULL)
+ *charset = '\0';
+ }
+
+ /* 1413 says don't do this -- but it's broken otherwise */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+
+ /* p now points to the authenticated name -- copy carefully */
+ if (strncasecmp(ostype, "other", 5) == 0 &&
+ (ostype[5] == ' ' || ostype[5] == '\0'))
+ {
+ snprintf(hbuf, sizeof hbuf, "IDENT:");
+ cleanstrcpy(&hbuf[6], p, MAXNAME);
+ }
+ else
+ cleanstrcpy(hbuf, p, MAXNAME);
+ i = strlen(hbuf);
+ snprintf(&hbuf[i], sizeof hbuf - i, "@%s",
+ RealHostName == NULL ? "localhost" : RealHostName);
+ goto postident;
+
+closeident:
+ (void) close(s);
+ clrevent(ev);
+
+noident:
+ if (RealHostName == NULL)
+ {
+ if (tTd(9, 1))
+ printf("getauthinfo: NULL\n");
+ return NULL;
+ }
+ snprintf(hbuf, sizeof hbuf, "%s", RealHostName);
+
+postident:
+#if IP_SRCROUTE
+# ifndef GET_IPOPT_DST
+# define GET_IPOPT_DST(dst) (dst)
+# endif
+ /*
+ ** Extract IP source routing information.
+ **
+ ** Format of output for a connection from site a through b
+ ** through c to d:
+ ** loose: @site-c@site-b:site-a
+ ** strict: !@site-c@site-b:site-a
+ **
+ ** o - pointer within ipopt_list structure.
+ ** q - pointer within ls/ss rr route data
+ ** p - pointer to hbuf
+ */
+
+ if (RealHostAddr.sa.sa_family == AF_INET)
+ {
+ SOCKOPT_LEN_T ipoptlen;
+ int j;
+ u_char *q;
+ u_char *o;
+ int l;
+ struct in_addr addr;
+ struct ipoption ipopt;
+
+ ipoptlen = sizeof ipopt;
+ if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS,
+ (char *) &ipopt, &ipoptlen) < 0)
+ goto noipsr;
+ if (ipoptlen == 0)
+ goto noipsr;
+ o = (u_char *) ipopt.ipopt_list;
+ while (o != NULL && o < (u_char *) &ipopt + ipoptlen)
+ {
+ switch (*o)
+ {
+ case IPOPT_EOL:
+ o = NULL;
+ break;
+
+ case IPOPT_NOP:
+ o++;
+ break;
+
+ case IPOPT_SSRR:
+ case IPOPT_LSRR:
+ /*
+ ** Source routing.
+ ** o[0] is the option type (loose/strict).
+ ** o[1] is the length of this option,
+ ** including option type and
+ ** length.
+ ** o[2] is the pointer into the route
+ ** data.
+ ** o[3] begins the route data.
+ */
+
+ p = &hbuf[strlen(hbuf)];
+ l = sizeof hbuf - (hbuf - p) - 6;
+ snprintf(p, SPACELEFT(hbuf, p), " [%s@%.*s",
+ *o == IPOPT_SSRR ? "!" : "",
+ l > 240 ? 120 : l / 2,
+ inet_ntoa(GET_IPOPT_DST(ipopt.ipopt_dst)));
+ i = strlen(p);
+ p += i;
+ l -= strlen(p);
+
+ j = o[1] / sizeof(struct in_addr) - 1;
+
+ /* q skips length and router pointer to data */
+ q = &o[3];
+ for ( ; j >= 0; j--)
+ {
+ memcpy(&addr, q, sizeof(addr));
+ snprintf(p, SPACELEFT(hbuf, p),
+ "%c%.*s",
+ j != 0 ? '@' : ':',
+ l > 240 ? 120 :
+ j == 0 ? l : l / 2,
+ inet_ntoa(addr));
+ i = strlen(p);
+ p += i;
+ l -= i + 1;
+ q += sizeof(struct in_addr);
+ }
+ o += o[1];
+ break;
+
+ default:
+ /* Skip over option */
+ o += o[1];
+ break;
+ }
+ }
+ snprintf(p, SPACELEFT(hbuf, p), "]");
+ goto postipsr;
+ }
+
+noipsr:
+#endif
+ if (RealHostName != NULL && RealHostName[0] != '[')
+ {
+ p = &hbuf[strlen(hbuf)];
+ (void) snprintf(p, SPACELEFT(hbuf, p), " [%.100s]",
+ anynet_ntoa(&RealHostAddr));
+ }
+ if (*may_be_forged)
+ {
+ p = &hbuf[strlen(hbuf)];
+ (void) snprintf(p, SPACELEFT(hbuf, p), " (may be forged)");
+ }
+
+#if IP_SRCROUTE
+postipsr:
+#endif
+ if (tTd(9, 1))
+ printf("getauthinfo: %s\n", hbuf);
+ return hbuf;
+}
+ /*
+** HOST_MAP_LOOKUP -- turn a hostname into canonical form
+**
+** Parameters:
+** map -- a pointer to this map.
+** name -- the (presumably unqualified) hostname.
+** av -- unused -- for compatibility with other mapping
+** functions.
+** statp -- an exit status (out parameter) -- set to
+** EX_TEMPFAIL if the name server is unavailable.
+**
+** Returns:
+** The mapping, if found.
+** NULL if no mapping found.
+**
+** Side Effects:
+** Looks up the host specified in hbuf. If it is not
+** the canonical name for that host, return the canonical
+** name (unless MF_MATCHONLY is set, which will cause the
+** status only to be returned).
+*/
+
+char *
+host_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ register struct hostent *hp;
+ struct in_addr in_addr;
+ char *cp;
+ register STAB *s;
+ char hbuf[MAXNAME + 1];
+
+ /*
+ ** See if we have already looked up this name. If so, just
+ ** return it.
+ */
+
+ s = stab(name, ST_NAMECANON, ST_ENTER);
+ if (bitset(NCF_VALID, s->s_namecanon.nc_flags))
+ {
+ if (tTd(9, 1))
+ printf("host_map_lookup(%s) => CACHE %s\n",
+ name,
+ s->s_namecanon.nc_cname == NULL
+ ? "NULL"
+ : s->s_namecanon.nc_cname);
+ errno = s->s_namecanon.nc_errno;
+#if NAMED_BIND
+ h_errno = s->s_namecanon.nc_herrno;
+#endif
+ *statp = s->s_namecanon.nc_stat;
+ if (*statp == EX_TEMPFAIL)
+ {
+ CurEnv->e_status = "4.4.3";
+ message("851 %s: Name server timeout",
+ shortenstring(name, 33));
+ }
+ if (*statp != EX_OK)
+ return NULL;
+ if (s->s_namecanon.nc_cname == NULL)
+ {
+ syserr("host_map_lookup(%s): bogus NULL cache entry, errno = %d, h_errno = %d",
+ name,
+ s->s_namecanon.nc_errno,
+ s->s_namecanon.nc_herrno);
+ return NULL;
+ }
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ cp = map_rewrite(map, name, strlen(name), NULL);
+ else
+ cp = map_rewrite(map,
+ s->s_namecanon.nc_cname,
+ strlen(s->s_namecanon.nc_cname),
+ av);
+ return cp;
+ }
+
+ /*
+ ** If we are running without a regular network connection (usually
+ ** dial-on-demand) and we are just queueing, we want to avoid DNS
+ ** lookups because those could try to connect to a server.
+ */
+
+ if (CurEnv->e_sendmode == SM_DEFER)
+ {
+ if (tTd(9, 1))
+ printf("host_map_lookup(%s) => DEFERRED\n", name);
+ *statp = EX_TEMPFAIL;
+ return NULL;
+ }
+
+ /*
+ ** If first character is a bracket, then it is an address
+ ** lookup. Address is copied into a temporary buffer to
+ ** strip the brackets and to preserve name if address is
+ ** unknown.
+ */
+
+ if (*name != '[')
+ {
+ if (tTd(9, 1))
+ printf("host_map_lookup(%s) => ", name);
+ s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */
+ snprintf(hbuf, sizeof hbuf, "%s", name);
+ if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX))
+ {
+ if (tTd(9, 1))
+ printf("%s\n", hbuf);
+ s->s_namecanon.nc_stat = EX_OK;
+ s->s_namecanon.nc_cname = newstr(hbuf);
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ cp = map_rewrite(map, name, strlen(name), NULL);
+ else
+ cp = map_rewrite(map, hbuf, strlen(hbuf), av);
+ return cp;
+ }
+ else
+ {
+ s->s_namecanon.nc_errno = errno;
+#if NAMED_BIND
+ s->s_namecanon.nc_herrno = h_errno;
+ if (tTd(9, 1))
+ printf("FAIL (%d)\n", h_errno);
+ switch (h_errno)
+ {
+ case TRY_AGAIN:
+ if (UseNameServer)
+ {
+ CurEnv->e_status = "4.4.3";
+ message("851 %s: Name server timeout",
+ shortenstring(name, 33));
+ }
+ *statp = EX_TEMPFAIL;
+ break;
+
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ *statp = EX_NOHOST;
+ break;
+
+ case NO_RECOVERY:
+ *statp = EX_SOFTWARE;
+ break;
+
+ default:
+ *statp = EX_UNAVAILABLE;
+ break;
+ }
+#else
+ if (tTd(9, 1))
+ printf("FAIL\n");
+ *statp = EX_NOHOST;
+#endif
+ s->s_namecanon.nc_stat = *statp;
+ return NULL;
+ }
+ }
+ if ((cp = strchr(name, ']')) == NULL)
+ return (NULL);
+ *cp = '\0';
+ in_addr.s_addr = inet_addr(&name[1]);
+ *cp = ']';
+
+ /* nope -- ask the name server */
+ hp = sm_gethostbyaddr((char *)&in_addr, INADDRSZ, AF_INET);
+ s->s_namecanon.nc_errno = errno;
+#if NAMED_BIND
+ s->s_namecanon.nc_herrno = h_errno;
+#endif
+ s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */
+ if (hp == NULL)
+ {
+ s->s_namecanon.nc_stat = *statp = EX_NOHOST;
+ return (NULL);
+ }
+
+ /* found a match -- copy out */
+ hp->h_name = denlstring((char *) hp->h_name, TRUE, TRUE);
+ s->s_namecanon.nc_stat = *statp = EX_OK;
+ s->s_namecanon.nc_cname = newstr(hp->h_name);
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ cp = map_rewrite(map, name, strlen(name), NULL);
+ else
+ cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av);
+ return cp;
+}
+
+# else /* DAEMON */
+/* code for systems without sophisticated networking */
+
+/*
+** MYHOSTNAME -- stub version for case of no daemon code.
+**
+** Can't convert to upper case here because might be a UUCP name.
+**
+** Mark, you can change this to be anything you want......
+*/
+
+char **
+myhostname(hostbuf, size)
+ char hostbuf[];
+ int size;
+{
+ register FILE *f;
+
+ hostbuf[0] = '\0';
+ f = fopen("/usr/include/whoami", "r");
+ if (f != NULL)
+ {
+ (void) fgets(hostbuf, size, f);
+ fixcrlf(hostbuf, TRUE);
+ (void) fclose(f);
+ }
+ return (NULL);
+}
+ /*
+** GETAUTHINFO -- get the real host name asociated with a file descriptor
+**
+** Parameters:
+** fd -- the descriptor
+** may_be_forged -- an outage that is set to TRUE if the
+** forward lookup of RealHostName does not match
+** RealHostAddr; set to FALSE if they do match.
+**
+** Returns:
+** The host name associated with this descriptor, if it can
+** be determined.
+** NULL otherwise.
+**
+** Side Effects:
+** none
+*/
+
+char *
+getauthinfo(fd, may_be_forged)
+ int fd;
+ bool *may_be_forged;
+{
+ *may_be_forged = FALSE;
+ return NULL;
+}
+ /*
+** MAPHOSTNAME -- turn a hostname into canonical form
+**
+** Parameters:
+** map -- a pointer to the database map.
+** name -- a buffer containing a hostname.
+** avp -- a pointer to a (cf file defined) argument vector.
+** statp -- an exit status (out parameter).
+**
+** Returns:
+** mapped host name
+** FALSE otherwise.
+**
+** Side Effects:
+** Looks up the host specified in name. If it is not
+** the canonical name for that host, replace it with
+** the canonical name. If the name is unknown, or it
+** is already the canonical name, leave it unchanged.
+*/
+
+/*ARGSUSED*/
+char *
+host_map_lookup(map, name, avp, statp)
+ MAP *map;
+ char *name;
+ char **avp;
+ char *statp;
+{
+ register struct hostent *hp;
+ char *cp;
+
+ hp = sm_gethostbyname(name);
+ if (hp == NULL)
+ {
+ *statp = EX_NOHOST;
+ return NULL;
+ }
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ cp = map_rewrite(map, name, strlen(name), NULL);
+ else
+ cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), avp);
+ return cp;
+}
+
+#endif /* DAEMON */
+ /*
+** HOST_MAP_INIT -- initialize host class structures
+*/
+
+bool
+host_map_init(map, args)
+ MAP *map;
+ char *args;
+{
+ register char *p = args;
+
+ for (;;)
+ {
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p != '-')
+ break;
+ switch (*++p)
+ {
+ case 'a':
+ map->map_app = ++p;
+ break;
+
+ case 'T':
+ map->map_tapp = ++p;
+ break;
+
+ case 'm':
+ map->map_mflags |= MF_MATCHONLY;
+ break;
+
+ case 't':
+ map->map_mflags |= MF_NODEFER;
+ break;
+ }
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+ if (map->map_app != NULL)
+ map->map_app = newstr(map->map_app);
+ if (map->map_tapp != NULL)
+ map->map_tapp = newstr(map->map_tapp);
+ return TRUE;
+}
+ /*
+** ANYNET_NTOA -- convert a network address to printable form.
+**
+** Parameters:
+** sap -- a pointer to a sockaddr structure.
+**
+** Returns:
+** A printable version of that sockaddr.
+*/
+
+#ifdef USE_SOCK_STREAM
+
+#if NETLINK
+# include <net/if_dl.h>
+#endif
+
+char *
+anynet_ntoa(sap)
+ register SOCKADDR *sap;
+{
+ register char *bp;
+ register char *ap;
+ int l;
+ static char buf[100];
+
+ /* check for null/zero family */
+ if (sap == NULL)
+ return "NULLADDR";
+ if (sap->sa.sa_family == 0)
+ return "0";
+
+ switch (sap->sa.sa_family)
+ {
+#if NETUNIX
+ case AF_UNIX:
+ if (sap->sunix.sun_path[0] != '\0')
+ snprintf(buf, sizeof buf, "[UNIX: %.64s]",
+ sap->sunix.sun_path);
+ else
+ snprintf(buf, sizeof buf, "[UNIX: localhost]");
+ return buf;
+#endif
+
+#if NETINET
+ case AF_INET:
+ return inet_ntoa(sap->sin.sin_addr);
+#endif
+
+#if NETLINK
+ case AF_LINK:
+ snprintf(buf, sizeof buf, "[LINK: %s]",
+ link_ntoa((struct sockaddr_dl *) &sap->sa));
+ return buf;
+#endif
+ default:
+ /* this case is needed when nothing is #defined */
+ /* in order to keep the switch syntactically correct */
+ break;
+ }
+
+ /* unknown family -- just dump bytes */
+ (void) snprintf(buf, sizeof buf, "Family %d: ", sap->sa.sa_family);
+ bp = &buf[strlen(buf)];
+ ap = sap->sa.sa_data;
+ for (l = sizeof sap->sa.sa_data; --l >= 0; )
+ {
+ (void) snprintf(bp, SPACELEFT(buf, bp), "%02x:", *ap++ & 0377);
+ bp += 3;
+ }
+ *--bp = '\0';
+ return buf;
+}
+ /*
+** HOSTNAMEBYANYADDR -- return name of host based on address
+**
+** Parameters:
+** sap -- SOCKADDR pointer
+**
+** Returns:
+** text representation of host name.
+**
+** Side Effects:
+** none.
+*/
+
+char *
+hostnamebyanyaddr(sap)
+ register SOCKADDR *sap;
+{
+ register struct hostent *hp;
+ int saveretry;
+
+#if NAMED_BIND
+ /* shorten name server timeout to avoid higher level timeouts */
+ saveretry = _res.retry;
+ _res.retry = 3;
+#endif /* NAMED_BIND */
+
+ switch (sap->sa.sa_family)
+ {
+#if NETINET
+ case AF_INET:
+ hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr,
+ INADDRSZ,
+ AF_INET);
+ break;
+#endif
+
+#if NETISO
+ case AF_ISO:
+ hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr,
+ sizeof sap->siso.siso_addr,
+ AF_ISO);
+ break;
+#endif
+
+#if NETUNIX
+ case AF_UNIX:
+ hp = NULL;
+ break;
+#endif
+
+ default:
+ hp = sm_gethostbyaddr(sap->sa.sa_data,
+ sizeof sap->sa.sa_data,
+ sap->sa.sa_family);
+ break;
+ }
+
+#if NAMED_BIND
+ _res.retry = saveretry;
+#endif /* NAMED_BIND */
+
+ if (hp != NULL && hp->h_name[0] != '[' &&
+ inet_addr(hp->h_name) == INADDR_NONE)
+ return denlstring((char *) hp->h_name, TRUE, TRUE);
+#if NETUNIX
+ else if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
+ return "localhost";
+#endif
+ else
+ {
+ /* produce a dotted quad */
+ static char buf[203];
+
+ (void) snprintf(buf, sizeof buf, "[%.200s]", anynet_ntoa(sap));
+ return buf;
+ }
+}
+
+#endif /* SOCK_STREAM */
diff --git a/contrib/sendmail/src/deliver.c b/contrib/sendmail/src/deliver.c
new file mode 100644
index 0000000..0e5eb07
--- /dev/null
+++ b/contrib/sendmail/src/deliver.c
@@ -0,0 +1,3722 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)deliver.c 8.353 (Berkeley) 6/30/98";
+#endif /* not lint */
+
+#include "sendmail.h"
+#include <errno.h>
+#include <grp.h>
+#if NAMED_BIND
+#include <resolv.h>
+#endif
+
+#if HASSETUSERCONTEXT
+# include <login_cap.h>
+#endif
+
+#if SMTP
+extern char SmtpError[];
+#endif
+
+/*
+** SENDALL -- actually send all the messages.
+**
+** Parameters:
+** e -- the envelope to send.
+** mode -- the delivery mode to use. If SM_DEFAULT, use
+** the current e->e_sendmode.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Scans the send lists and sends everything it finds.
+** Delivers any appropriate error messages.
+** If we are running in a non-interactive mode, takes the
+** appropriate action.
+*/
+
+void
+sendall(e, mode)
+ ENVELOPE *e;
+ int mode;
+{
+ register ADDRESS *q;
+ char *owner;
+ int otherowners;
+ register ENVELOPE *ee;
+ ENVELOPE *splitenv = NULL;
+ int oldverbose = Verbose;
+ bool somedeliveries = FALSE, expensive = FALSE;
+ pid_t pid;
+ void sendenvelope __P((ENVELOPE *, int));
+
+ /*
+ ** If this message is to be discarded, don't bother sending
+ ** the message at all.
+ */
+
+ if (bitset(EF_DISCARD, e->e_flags))
+ {
+ if (tTd(13, 1))
+ printf("sendall: discarding id %s\n", e->e_id);
+ e->e_flags |= EF_CLRQUEUE;
+ if (LogLevel > 4)
+ sm_syslog(LOG_INFO, e->e_id, "discarded");
+ markstats(e, NULL, TRUE);
+ return;
+ }
+
+ /*
+ ** If we have had global, fatal errors, don't bother sending
+ ** the message at all if we are in SMTP mode. Local errors
+ ** (e.g., a single address failing) will still cause the other
+ ** addresses to be sent.
+ */
+
+ if (bitset(EF_FATALERRS, e->e_flags) &&
+ (OpMode == MD_SMTP || OpMode == MD_DAEMON))
+ {
+ e->e_flags |= EF_CLRQUEUE;
+ return;
+ }
+
+ /* determine actual delivery mode */
+ if (mode == SM_DEFAULT)
+ {
+ mode = e->e_sendmode;
+ if (mode != SM_VERIFY && mode != SM_DEFER &&
+ shouldqueue(e->e_msgpriority, e->e_ctime))
+ mode = SM_QUEUE;
+ }
+
+ if (tTd(13, 1))
+ {
+ extern void printenvflags __P((ENVELOPE *));
+
+ printf("\n===== SENDALL: mode %c, id %s, e_from ",
+ mode, e->e_id);
+ printaddr(&e->e_from, FALSE);
+ printf("\te_flags = ");
+ printenvflags(e);
+ printf("sendqueue:\n");
+ printaddr(e->e_sendqueue, TRUE);
+ }
+
+ /*
+ ** Do any preprocessing necessary for the mode we are running.
+ ** Check to make sure the hop count is reasonable.
+ ** Delete sends to the sender in mailing lists.
+ */
+
+ CurEnv = e;
+ if (tTd(62, 1))
+ checkfds(NULL);
+
+ if (e->e_hopcount > MaxHopCount)
+ {
+ errno = 0;
+#if QUEUE
+ queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
+#endif
+ e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE;
+ syserr("554 Too many hops %d (%d max): from %s via %s, to %s",
+ e->e_hopcount, MaxHopCount, e->e_from.q_paddr,
+ RealHostName == NULL ? "localhost" : RealHostName,
+ e->e_sendqueue->q_paddr);
+ e->e_sendqueue->q_status = "5.4.6";
+ return;
+ }
+
+ /*
+ ** Do sender deletion.
+ **
+ ** If the sender has the QQUEUEUP flag set, skip this.
+ ** This can happen if the name server is hosed when you
+ ** are trying to send mail. The result is that the sender
+ ** is instantiated in the queue as a recipient.
+ */
+
+ if (!bitset(EF_METOO, e->e_flags) &&
+ !bitset(QQUEUEUP, e->e_from.q_flags))
+ {
+ if (tTd(13, 5))
+ {
+ printf("sendall: QDONTSEND ");
+ printaddr(&e->e_from, FALSE);
+ }
+ e->e_from.q_flags |= QDONTSEND;
+ (void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
+ }
+
+ /*
+ ** Handle alias owners.
+ **
+ ** We scan up the q_alias chain looking for owners.
+ ** We discard owners that are the same as the return path.
+ */
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ register struct address *a;
+
+ for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias)
+ continue;
+ if (a != NULL)
+ q->q_owner = a->q_owner;
+
+ if (q->q_owner != NULL &&
+ !bitset(QDONTSEND, q->q_flags) &&
+ strcmp(q->q_owner, e->e_from.q_paddr) == 0)
+ q->q_owner = NULL;
+ }
+
+ if (tTd(13, 25))
+ {
+ printf("\nAfter first owner pass, sendq =\n");
+ printaddr(e->e_sendqueue, TRUE);
+ }
+
+ owner = "";
+ otherowners = 1;
+ while (owner != NULL && otherowners > 0)
+ {
+ if (tTd(13, 28))
+ printf("owner = \"%s\", otherowners = %d\n",
+ owner, otherowners);
+ owner = NULL;
+ otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0;
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (tTd(13, 30))
+ {
+ printf("Checking ");
+ printaddr(q, FALSE);
+ }
+ if (bitset(QDONTSEND, q->q_flags))
+ {
+ if (tTd(13, 30))
+ printf(" ... QDONTSEND\n");
+ continue;
+ }
+ if (tTd(13, 29) && !tTd(13, 30))
+ {
+ printf("Checking ");
+ printaddr(q, FALSE);
+ }
+
+ if (q->q_owner != NULL)
+ {
+ if (owner == NULL)
+ {
+ if (tTd(13, 40))
+ printf(" ... First owner = \"%s\"\n",
+ q->q_owner);
+ owner = q->q_owner;
+ }
+ else if (owner != q->q_owner)
+ {
+ if (strcmp(owner, q->q_owner) == 0)
+ {
+ if (tTd(13, 40))
+ printf(" ... Same owner = \"%s\"\n",
+ owner);
+
+ /* make future comparisons cheap */
+ q->q_owner = owner;
+ }
+ else
+ {
+ if (tTd(13, 40))
+ printf(" ... Another owner \"%s\"\n",
+ q->q_owner);
+ otherowners++;
+ }
+ owner = q->q_owner;
+ }
+ else if (tTd(13, 40))
+ printf(" ... Same owner = \"%s\"\n",
+ owner);
+ }
+ else
+ {
+ if (tTd(13, 40))
+ printf(" ... Null owner\n");
+ otherowners++;
+ }
+
+ /*
+ ** If this mailer is expensive, and if we don't
+ ** want to make connections now, just mark these
+ ** addresses and return. This is useful if we
+ ** want to batch connections to reduce load. This
+ ** will cause the messages to be queued up, and a
+ ** daemon will come along to send the messages later.
+ */
+
+ if (bitset(QBADADDR|QQUEUEUP, q->q_flags))
+ {
+ if (tTd(13, 30))
+ printf(" ... QBADADDR|QQUEUEUP\n");
+ continue;
+ }
+ if (NoConnect && !Verbose &&
+ bitnset(M_EXPENSIVE, q->q_mailer->m_flags))
+ {
+ if (tTd(13, 30))
+ printf(" ... expensive\n");
+ q->q_flags |= QQUEUEUP;
+ expensive = TRUE;
+ }
+ else
+ {
+ if (tTd(13, 30))
+ printf(" ... deliverable\n");
+ somedeliveries = TRUE;
+ }
+ }
+
+ if (owner != NULL && otherowners > 0)
+ {
+ extern HDR *copyheader __P((HDR *));
+ extern ADDRESS *copyqueue __P((ADDRESS *));
+ extern void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int));
+
+ /*
+ ** Split this envelope into two.
+ */
+
+ ee = (ENVELOPE *) xalloc(sizeof(ENVELOPE));
+ *ee = *e;
+ ee->e_id = NULL;
+ (void) queuename(ee, '\0');
+
+ if (tTd(13, 1))
+ printf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n",
+ e->e_id, ee->e_id, owner, otherowners);
+
+ ee->e_header = copyheader(e->e_header);
+ ee->e_sendqueue = copyqueue(e->e_sendqueue);
+ ee->e_errorqueue = copyqueue(e->e_errorqueue);
+ ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM);
+ ee->e_flags |= EF_NORECEIPT;
+ setsender(owner, ee, NULL, '\0', TRUE);
+ if (tTd(13, 5))
+ {
+ printf("sendall(split): QDONTSEND ");
+ printaddr(&ee->e_from, FALSE);
+ }
+ ee->e_from.q_flags |= QDONTSEND;
+ ee->e_dfp = NULL;
+ ee->e_xfp = NULL;
+ ee->e_errormode = EM_MAIL;
+ ee->e_sibling = splitenv;
+ splitenv = ee;
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (q->q_owner == owner)
+ {
+ q->q_flags |= QDONTSEND;
+ q->q_flags &= ~(QQUEUEUP|QBADADDR);
+ if (tTd(13, 6))
+ printf("\t... stripping %s from original envelope\n",
+ q->q_paddr);
+ }
+ }
+ for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (q->q_owner != owner)
+ {
+ q->q_flags |= QDONTSEND;
+ q->q_flags &= ~(QQUEUEUP|QBADADDR);
+ if (tTd(13, 6))
+ printf("\t... dropping %s from cloned envelope\n",
+ q->q_paddr);
+ }
+ else
+ {
+ /* clear DSN parameters */
+ q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
+ q->q_flags |= DefaultNotify & ~QPINGONSUCCESS;
+ if (tTd(13, 6))
+ printf("\t... moving %s to cloned envelope\n",
+ q->q_paddr);
+ }
+ }
+
+ if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
+ dup_queue_file(e, ee, 'd');
+ openxscript(ee);
+ if (LogLevel > 4)
+ sm_syslog(LOG_INFO, ee->e_id,
+ "clone %s, owner=%s",
+ e->e_id, owner);
+ }
+ }
+
+ if (owner != NULL)
+ {
+ setsender(owner, e, NULL, '\0', TRUE);
+ if (tTd(13, 5))
+ {
+ printf("sendall(owner): QDONTSEND ");
+ printaddr(&e->e_from, FALSE);
+ }
+ e->e_from.q_flags |= QDONTSEND;
+ e->e_errormode = EM_MAIL;
+ e->e_flags |= EF_NORECEIPT;
+ e->e_flags &= ~EF_FATALERRS;
+ }
+
+ /* if nothing to be delivered, just queue up everything */
+ if (!somedeliveries && mode != SM_QUEUE && mode != SM_DEFER &&
+ mode != SM_VERIFY)
+ {
+ if (tTd(13, 29))
+ printf("No deliveries: auto-queuing\n");
+ mode = SM_QUEUE;
+
+ /* treat this as a delivery in terms of counting tries */
+ e->e_dtime = curtime();
+ if (!expensive)
+ e->e_ntries++;
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ {
+ ee->e_dtime = curtime();
+ if (!expensive)
+ ee->e_ntries++;
+ }
+ }
+
+# if QUEUE
+ if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK ||
+ (mode != SM_VERIFY && SuperSafe)) &&
+ (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL))
+ {
+ /* be sure everything is instantiated in the queue */
+ queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ queueup(ee, mode == SM_QUEUE || mode == SM_DEFER);
+ }
+#endif /* QUEUE */
+
+ if (tTd(62, 10))
+ checkfds("after envelope splitting");
+
+ /*
+ ** If we belong in background, fork now.
+ */
+
+ if (tTd(13, 20))
+ {
+ printf("sendall: final mode = %c\n", mode);
+ if (tTd(13, 21))
+ {
+ printf("\n================ Final Send Queue(s) =====================\n");
+ printf("\n *** Envelope %s, e_from=%s ***\n",
+ e->e_id, e->e_from.q_paddr);
+ printaddr(e->e_sendqueue, TRUE);
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ {
+ printf("\n *** Envelope %s, e_from=%s ***\n",
+ ee->e_id, ee->e_from.q_paddr);
+ printaddr(ee->e_sendqueue, TRUE);
+ }
+ printf("==========================================================\n\n");
+ }
+ }
+ switch (mode)
+ {
+ case SM_VERIFY:
+ Verbose = 2;
+ break;
+
+ case SM_QUEUE:
+ case SM_DEFER:
+# if HASFLOCK
+ queueonly:
+# endif
+ if (e->e_nrcpts > 0)
+ e->e_flags |= EF_INQUEUE;
+ dropenvelope(e, splitenv != NULL);
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ {
+ if (ee->e_nrcpts > 0)
+ ee->e_flags |= EF_INQUEUE;
+ dropenvelope(ee, FALSE);
+ }
+ return;
+
+ case SM_FORK:
+ if (e->e_xfp != NULL)
+ (void) fflush(e->e_xfp);
+
+# if !HASFLOCK
+ /*
+ ** Since fcntl locking has the interesting semantic that
+ ** the lock is owned by a process, not by an open file
+ ** descriptor, we have to flush this to the queue, and
+ ** then restart from scratch in the child.
+ */
+
+ {
+ /* save id for future use */
+ char *qid = e->e_id;
+
+ /* now drop the envelope in the parent */
+ e->e_flags |= EF_INQUEUE;
+ dropenvelope(e, splitenv != NULL);
+
+ /* arrange to reacquire lock after fork */
+ e->e_id = qid;
+ }
+
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ {
+ /* save id for future use */
+ char *qid = ee->e_id;
+
+ /* drop envelope in parent */
+ ee->e_flags |= EF_INQUEUE;
+ dropenvelope(ee, FALSE);
+
+ /* and save qid for reacquisition */
+ ee->e_id = qid;
+ }
+
+# endif /* !HASFLOCK */
+
+ pid = fork();
+ if (pid < 0)
+ {
+# if HASFLOCK
+ goto queueonly;
+# else
+ e->e_id = NULL;
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ ee->e_id = NULL;
+ return;
+# endif /* HASFLOCK */
+ }
+ else if (pid > 0)
+ {
+# if HASFLOCK
+ /* be sure we leave the temp files to our child */
+ /* close any random open files in the envelope */
+ closexscript(e);
+ if (e->e_dfp != NULL)
+ (void) xfclose(e->e_dfp, "sendenvelope dfp", e->e_id);
+ e->e_dfp = NULL;
+ e->e_flags &= ~EF_HAS_DF;
+
+ /* can't call unlockqueue to avoid unlink of xfp */
+ if (e->e_lockfp != NULL)
+ (void) xfclose(e->e_lockfp, "sendenvelope lockfp", e->e_id);
+ e->e_lockfp = NULL;
+# endif
+
+ /* make sure the parent doesn't own the envelope */
+ e->e_id = NULL;
+
+ /* catch intermediate zombie */
+ (void) waitfor(pid);
+ return;
+ }
+
+ /* double fork to avoid zombies */
+ pid = fork();
+ if (pid > 0)
+ exit(EX_OK);
+
+ /* be sure we are immune from the terminal */
+ disconnect(2, e);
+
+ /* prevent parent from waiting if there was an error */
+ if (pid < 0)
+ {
+# if HASFLOCK
+ e->e_flags |= EF_INQUEUE;
+# else
+ e->e_id = NULL;
+# endif /* HASFLOCK */
+ finis();
+ }
+
+ /* be sure to give error messages in child */
+ QuickAbort = FALSE;
+
+ /*
+ ** Close any cached connections.
+ **
+ ** We don't send the QUIT protocol because the parent
+ ** still knows about the connection.
+ **
+ ** This should only happen when delivering an error
+ ** message.
+ */
+
+ mci_flush(FALSE, NULL);
+
+# if HASFLOCK
+ break;
+# else
+
+ /*
+ ** Now reacquire and run the various queue files.
+ */
+
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ {
+ ENVELOPE *sibling = ee->e_sibling;
+
+ (void) dowork(ee->e_id, FALSE, FALSE, ee);
+ ee->e_sibling = sibling;
+ }
+ (void) dowork(e->e_id, FALSE, FALSE, e);
+ finis();
+# endif /* !HASFLOCK */
+ }
+
+ sendenvelope(e, mode);
+ dropenvelope(e, TRUE);
+ for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+ {
+ CurEnv = ee;
+ if (mode != SM_VERIFY)
+ openxscript(ee);
+ sendenvelope(ee, mode);
+ dropenvelope(ee, TRUE);
+ }
+ CurEnv = e;
+
+ Verbose = oldverbose;
+ if (mode == SM_FORK)
+ finis();
+}
+
+void
+sendenvelope(e, mode)
+ register ENVELOPE *e;
+ int mode;
+{
+ register ADDRESS *q;
+ bool didany;
+
+ if (tTd(13, 10))
+ printf("sendenvelope(%s) e_flags=0x%lx\n",
+ e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
+ e->e_flags);
+ if (LogLevel > 80)
+ sm_syslog(LOG_DEBUG, e->e_id,
+ "sendenvelope, flags=0x%x",
+ e->e_flags);
+
+ /*
+ ** If we have had global, fatal errors, don't bother sending
+ ** the message at all if we are in SMTP mode. Local errors
+ ** (e.g., a single address failing) will still cause the other
+ ** addresses to be sent.
+ */
+
+ if (bitset(EF_FATALERRS, e->e_flags) &&
+ (OpMode == MD_SMTP || OpMode == MD_DAEMON))
+ {
+ e->e_flags |= EF_CLRQUEUE;
+ return;
+ }
+
+ /*
+ ** Run through the list and send everything.
+ **
+ ** Set EF_GLOBALERRS so that error messages during delivery
+ ** result in returned mail.
+ */
+
+ e->e_nsent = 0;
+ e->e_flags |= EF_GLOBALERRS;
+ define(macid("{envid}", NULL), e->e_envid, e);
+ define(macid("{bodytype}", NULL), e->e_bodytype, e);
+ didany = FALSE;
+
+ /* now run through the queue */
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+#if XDEBUG
+ char wbuf[MAXNAME + 20];
+
+ (void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)",
+ MAXNAME, q->q_paddr);
+ checkfd012(wbuf);
+#endif
+ if (mode == SM_VERIFY)
+ {
+ e->e_to = q->q_paddr;
+ if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
+ {
+ if (q->q_host != NULL && q->q_host[0] != '\0')
+ message("deliverable: mailer %s, host %s, user %s",
+ q->q_mailer->m_name,
+ q->q_host,
+ q->q_user);
+ else
+ message("deliverable: mailer %s, user %s",
+ q->q_mailer->m_name,
+ q->q_user);
+ }
+ }
+ else if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
+ {
+ extern int deliver __P((ENVELOPE *, ADDRESS *));
+
+# if QUEUE
+ /*
+ ** Checkpoint the send list every few addresses
+ */
+
+ if (e->e_nsent >= CheckpointInterval)
+ {
+ queueup(e, FALSE);
+ e->e_nsent = 0;
+ }
+# endif /* QUEUE */
+ (void) deliver(e, q);
+ didany = TRUE;
+ }
+ }
+ if (didany)
+ {
+ e->e_dtime = curtime();
+ e->e_ntries++;
+ }
+
+#if XDEBUG
+ checkfd012("end of sendenvelope");
+#endif
+}
+ /*
+** DUP_QUEUE_FILE -- duplicate a queue file into a split queue
+**
+** Parameters:
+** e -- the existing envelope
+** ee -- the new envelope
+** type -- the queue file type (e.g., 'd')
+**
+** Returns:
+** none
+*/
+
+void
+dup_queue_file(e, ee, type)
+ struct envelope *e, *ee;
+ int type;
+{
+ char f1buf[MAXQFNAME], f2buf[MAXQFNAME];
+
+ ee->e_dfp = NULL;
+ ee->e_xfp = NULL;
+ snprintf(f1buf, sizeof f1buf, "%s", queuename(e, type));
+ snprintf(f2buf, sizeof f2buf, "%s", queuename(ee, type));
+ if (link(f1buf, f2buf) < 0)
+ {
+ int saverrno = errno;
+
+ syserr("sendall: link(%s, %s)", f1buf, f2buf);
+ if (saverrno == EEXIST)
+ {
+ if (unlink(f2buf) < 0)
+ {
+ syserr("!sendall: unlink(%s): permanent",
+ f2buf);
+ /*NOTREACHED*/
+ }
+ if (link(f1buf, f2buf) < 0)
+ {
+ syserr("!sendall: link(%s, %s): permanent",
+ f1buf, f2buf);
+ /*NOTREACHED*/
+ }
+ }
+ }
+}
+ /*
+** DOFORK -- do a fork, retrying a couple of times on failure.
+**
+** This MUST be a macro, since after a vfork we are running
+** two processes on the same stack!!!
+**
+** Parameters:
+** none.
+**
+** Returns:
+** From a macro??? You've got to be kidding!
+**
+** Side Effects:
+** Modifies the ==> LOCAL <== variable 'pid', leaving:
+** pid of child in parent, zero in child.
+** -1 on unrecoverable error.
+**
+** Notes:
+** I'm awfully sorry this looks so awful. That's
+** vfork for you.....
+*/
+
+# define NFORKTRIES 5
+
+# ifndef FORK
+# define FORK fork
+# endif
+
+# define DOFORK(fORKfN) \
+{\
+ register int i;\
+\
+ for (i = NFORKTRIES; --i >= 0; )\
+ {\
+ pid = fORKfN();\
+ if (pid >= 0)\
+ break;\
+ if (i > 0)\
+ sleep((unsigned) NFORKTRIES - i);\
+ }\
+}
+ /*
+** DOFORK -- simple fork interface to DOFORK.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** pid of child in parent.
+** zero in child.
+** -1 on error.
+**
+** Side Effects:
+** returns twice, once in parent and once in child.
+*/
+
+int
+dofork()
+{
+ register pid_t pid = -1;
+
+ DOFORK(fork);
+ return (pid);
+}
+ /*
+** DELIVER -- Deliver a message to a list of addresses.
+**
+** This routine delivers to everyone on the same host as the
+** user on the head of the list. It is clever about mailers
+** that don't handle multiple users. It is NOT guaranteed
+** that it will deliver to all these addresses however -- so
+** deliver should be called once for each address on the
+** list.
+**
+** Parameters:
+** e -- the envelope to deliver.
+** firstto -- head of the address list to deliver to.
+**
+** Returns:
+** zero -- successfully delivered.
+** else -- some failure, see ExitStat for more info.
+**
+** Side Effects:
+** The standard input is passed off to someone.
+*/
+
+#ifndef NO_UID
+# define NO_UID -1
+#endif
+#ifndef NO_GID
+# define NO_GID -1
+#endif
+
+int
+deliver(e, firstto)
+ register ENVELOPE *e;
+ ADDRESS *firstto;
+{
+ char *host; /* host being sent to */
+ char *user; /* user being sent to */
+ char **pvp;
+ register char **mvp;
+ register char *p;
+ register MAILER *m; /* mailer for this recipient */
+ ADDRESS *volatile ctladdr;
+ ADDRESS *volatile contextaddr = NULL;
+ register MCI *volatile mci;
+ register ADDRESS *to = firstto;
+ volatile bool clever = FALSE; /* running user smtp to this mailer */
+ ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
+ int rcode; /* response code */
+ int lmtp_rcode = EX_OK;
+ char *firstsig; /* signature of firstto */
+ pid_t pid = -1;
+ char *volatile curhost;
+ register u_short port = 0;
+ time_t xstart;
+ bool suidwarn;
+ bool anyok; /* at least one address was OK */
+ bool goodmxfound = FALSE; /* at least one MX was OK */
+ int mpvect[2];
+ int rpvect[2];
+ char *pv[MAXPV+1];
+ char tobuf[TOBUFSIZE]; /* text line of to people */
+ char buf[MAXNAME + 1];
+ char rpathbuf[MAXNAME + 1]; /* translated return path */
+ extern int checkcompat __P((ADDRESS *, ENVELOPE *));
+ extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int));
+
+ errno = 0;
+ if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags))
+ return (0);
+
+ suidwarn = geteuid() == 0;
+
+#if NAMED_BIND
+ /* unless interactive, try twice, over a minute */
+ if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
+ {
+ _res.retrans = 30;
+ _res.retry = 2;
+ }
+#endif
+
+ m = to->q_mailer;
+ host = to->q_host;
+ CurEnv = e; /* just in case */
+ e->e_statmsg = NULL;
+#if SMTP
+ SmtpError[0] = '\0';
+#endif
+ xstart = curtime();
+
+ if (tTd(10, 1))
+ printf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
+ e->e_id, m->m_name, host, to->q_user);
+ if (tTd(10, 100))
+ printopenfds(FALSE);
+
+ /*
+ ** Clear $&{client_*} macros if this is a bounce message to
+ ** prevent rejection by check_compat ruleset.
+ */
+
+ if (bitset(EF_RESPONSE, e->e_flags))
+ {
+ define(macid("{client_name}", NULL), "", e);
+ define(macid("{client_addr}", NULL), "", e);
+ define(macid("{client_port}", NULL), "", e);
+ }
+
+ /*
+ ** Do initial argv setup.
+ ** Insert the mailer name. Notice that $x expansion is
+ ** NOT done on the mailer name. Then, if the mailer has
+ ** a picky -f flag, we insert it as appropriate. This
+ ** code does not check for 'pv' overflow; this places a
+ ** manifest lower limit of 4 for MAXPV.
+ ** The from address rewrite is expected to make
+ ** the address relative to the other end.
+ */
+
+ /* rewrite from address, using rewriting rules */
+ rcode = EX_OK;
+ if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
+ p = e->e_sender;
+ else
+ p = e->e_from.q_paddr;
+ p = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e);
+ if (strlen(p) >= (SIZE_T) sizeof rpathbuf)
+ {
+ p = shortenstring(p, MAXSHORTSTR);
+ syserr("remotename: huge return %s", p);
+ }
+ snprintf(rpathbuf, sizeof rpathbuf, "%s", p);
+ define('g', rpathbuf, e); /* translated return path */
+ define('h', host, e); /* to host */
+ Errors = 0;
+ pvp = pv;
+ *pvp++ = m->m_argv[0];
+
+ /* insert -f or -r flag as appropriate */
+ if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags)))
+ {
+ if (bitnset(M_FOPT, m->m_flags))
+ *pvp++ = "-f";
+ else
+ *pvp++ = "-r";
+ *pvp++ = newstr(rpathbuf);
+ }
+
+ /*
+ ** Append the other fixed parts of the argv. These run
+ ** up to the first entry containing "$u". There can only
+ ** be one of these, and there are only a few more slots
+ ** in the pv after it.
+ */
+
+ for (mvp = m->m_argv; (p = *++mvp) != NULL; )
+ {
+ /* can't use strchr here because of sign extension problems */
+ while (*p != '\0')
+ {
+ if ((*p++ & 0377) == MACROEXPAND)
+ {
+ if (*p == 'u')
+ break;
+ }
+ }
+
+ if (*p != '\0')
+ break;
+
+ /* this entry is safe -- go ahead and process it */
+ expand(*mvp, buf, sizeof buf, e);
+ *pvp++ = newstr(buf);
+ if (pvp >= &pv[MAXPV - 3])
+ {
+ syserr("554 Too many parameters to %s before $u", pv[0]);
+ return (-1);
+ }
+ }
+
+ /*
+ ** If we have no substitution for the user name in the argument
+ ** list, we know that we must supply the names otherwise -- and
+ ** SMTP is the answer!!
+ */
+
+ if (*mvp == NULL)
+ {
+ /* running SMTP */
+# if SMTP
+ clever = TRUE;
+ *pvp = NULL;
+# else /* SMTP */
+ /* oops! we don't implement SMTP */
+ syserr("554 SMTP style mailer not implemented");
+ return (EX_SOFTWARE);
+# endif /* SMTP */
+ }
+
+ /*
+ ** At this point *mvp points to the argument with $u. We
+ ** run through our address list and append all the addresses
+ ** we can. If we run out of space, do not fret! We can
+ ** always send another copy later.
+ */
+
+ tobuf[0] = '\0';
+ e->e_to = tobuf;
+ ctladdr = NULL;
+ firstsig = hostsignature(firstto->q_mailer, firstto->q_host, e);
+ for (; to != NULL; to = to->q_next)
+ {
+ /* avoid sending multiple recipients to dumb mailers */
+ if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
+ break;
+
+ /* if already sent or not for this host, don't send */
+ if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags) ||
+ to->q_mailer != firstto->q_mailer ||
+ strcmp(hostsignature(to->q_mailer, to->q_host, e), firstsig) != 0)
+ continue;
+
+ /* avoid overflowing tobuf */
+ if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2))
+ break;
+
+ if (tTd(10, 1))
+ {
+ printf("\nsend to ");
+ printaddr(to, FALSE);
+ }
+
+ /* compute effective uid/gid when sending */
+ if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
+ contextaddr = ctladdr = getctladdr(to);
+
+ if (tTd(10, 2))
+ {
+ printf("ctladdr=");
+ printaddr(ctladdr, FALSE);
+ }
+
+ user = to->q_user;
+ e->e_to = to->q_paddr;
+ if (tTd(10, 5))
+ {
+ printf("deliver: QDONTSEND ");
+ printaddr(to, FALSE);
+ }
+ to->q_flags |= QDONTSEND;
+
+ /*
+ ** Check to see that these people are allowed to
+ ** talk to each other.
+ */
+
+ if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
+ {
+ e->e_flags |= EF_NO_BODY_RETN;
+ if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags))
+ to->q_status = "5.2.3";
+ else
+ to->q_status = "5.3.4";
+ usrerr("552 Message is too large; %ld bytes max", m->m_maxsize);
+ markfailure(e, to, NULL, EX_UNAVAILABLE);
+ giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, xstart, e);
+ continue;
+ }
+#if NAMED_BIND
+ h_errno = 0;
+#endif
+
+ /* do config file checking of compatibility */
+ rcode = rscheck("check_compat",
+ e->e_from.q_paddr, to->q_paddr, e);
+ if (rcode == EX_OK)
+ {
+ /* do in-code checking */
+ rcode = checkcompat(to, e);
+ }
+ if (rcode != EX_OK)
+ {
+ markfailure(e, to, NULL, rcode);
+ giveresponse(rcode, m, NULL, ctladdr, xstart, e);
+ continue;
+ }
+
+ /*
+ ** Strip quote bits from names if the mailer is dumb
+ ** about them.
+ */
+
+ if (bitnset(M_STRIPQ, m->m_flags))
+ {
+ stripquotes(user);
+ stripquotes(host);
+ }
+
+ /* hack attack -- delivermail compatibility */
+ if (m == ProgMailer && *user == '|')
+ user++;
+
+ /*
+ ** If an error message has already been given, don't
+ ** bother to send to this address.
+ **
+ ** >>>>>>>>>> This clause assumes that the local mailer
+ ** >> NOTE >> cannot do any further aliasing; that
+ ** >>>>>>>>>> function is subsumed by sendmail.
+ */
+
+ if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
+ continue;
+
+ /*
+ ** See if this user name is "special".
+ ** If the user name has a slash in it, assume that this
+ ** is a file -- send it off without further ado. Note
+ ** that this type of addresses is not processed along
+ ** with the others, so we fudge on the To person.
+ */
+
+ if (strcmp(m->m_mailer, "[FILE]") == 0)
+ {
+ define('u', user, e); /* to user */
+ p = to->q_home;
+ if (p == NULL && ctladdr != NULL)
+ p = ctladdr->q_home;
+ define('z', p, e); /* user's home */
+ expand(m->m_argv[1], buf, sizeof buf, e);
+ if (strlen(buf) > 0)
+ rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e);
+ else
+ {
+ syserr("empty filename specification for mailer %s",
+ m->m_name);
+ rcode = EX_CONFIG;
+ }
+ giveresponse(rcode, m, NULL, ctladdr, xstart, e);
+ markfailure(e, to, NULL, rcode);
+ e->e_nsent++;
+ if (rcode == EX_OK)
+ {
+ to->q_flags |= QSENT;
+ if (bitnset(M_LOCALMAILER, m->m_flags) &&
+ bitset(QPINGONSUCCESS, to->q_flags))
+ {
+ to->q_flags |= QDELIVERED;
+ to->q_status = "2.1.5";
+ fprintf(e->e_xfp, "%s... Successfully delivered\n",
+ to->q_paddr);
+ }
+ }
+ to->q_statdate = curtime();
+ markstats(e, to, FALSE);
+ continue;
+ }
+
+ /*
+ ** Address is verified -- add this user to mailer
+ ** argv, and add it to the print list of recipients.
+ */
+
+ /* link together the chain of recipients */
+ to->q_tchain = tochain;
+ tochain = to;
+
+ /* create list of users for error messages */
+ (void) strcat(tobuf, ",");
+ (void) strcat(tobuf, to->q_paddr);
+ define('u', user, e); /* to user */
+ p = to->q_home;
+ if (p == NULL && ctladdr != NULL)
+ p = ctladdr->q_home;
+ define('z', p, e); /* user's home */
+
+ /*
+ ** Expand out this user into argument list.
+ */
+
+ if (!clever)
+ {
+ expand(*mvp, buf, sizeof buf, e);
+ *pvp++ = newstr(buf);
+ if (pvp >= &pv[MAXPV - 2])
+ {
+ /* allow some space for trailing parms */
+ break;
+ }
+ }
+ }
+
+ /* see if any addresses still exist */
+ if (tobuf[0] == '\0')
+ {
+ define('g', (char *) NULL, e);
+ return (0);
+ }
+
+ /* print out messages as full list */
+ e->e_to = tobuf + 1;
+
+ /*
+ ** Fill out any parameters after the $u parameter.
+ */
+
+ while (!clever && *++mvp != NULL)
+ {
+ expand(*mvp, buf, sizeof buf, e);
+ *pvp++ = newstr(buf);
+ if (pvp >= &pv[MAXPV])
+ syserr("554 deliver: pv overflow after $u for %s", pv[0]);
+ }
+ *pvp++ = NULL;
+
+ /*
+ ** Call the mailer.
+ ** The argument vector gets built, pipes
+ ** are created as necessary, and we fork & exec as
+ ** appropriate.
+ ** If we are running SMTP, we just need to clean up.
+ */
+
+ /*XXX this seems a bit wierd */
+ if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
+ bitset(QGOODUID, e->e_from.q_flags))
+ ctladdr = &e->e_from;
+
+#if NAMED_BIND
+ if (ConfigLevel < 2)
+ _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */
+#endif
+
+ if (tTd(11, 1))
+ {
+ printf("openmailer:");
+ printav(pv);
+ }
+ errno = 0;
+#if NAMED_BIND
+ h_errno = 0;
+#endif
+
+ CurHostName = NULL;
+
+ /*
+ ** Deal with the special case of mail handled through an IPC
+ ** connection.
+ ** In this case we don't actually fork. We must be
+ ** running SMTP for this to work. We will return a
+ ** zero pid to indicate that we are running IPC.
+ ** We also handle a debug version that just talks to stdin/out.
+ */
+
+ curhost = NULL;
+ SmtpPhase = NULL;
+ mci = NULL;
+
+#if XDEBUG
+ {
+ char wbuf[MAXLINE];
+
+ /* make absolutely certain 0, 1, and 2 are in use */
+ snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)",
+ shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
+ checkfd012(wbuf);
+ }
+#endif
+
+ /* check for 8-bit available */
+ if (bitset(EF_HAS8BIT, e->e_flags) &&
+ bitnset(M_7BITS, m->m_flags) &&
+ (bitset(EF_DONT_MIME, e->e_flags) ||
+ !(bitset(MM_MIME8BIT, MimeMode) ||
+ (bitset(EF_IS_MIME, e->e_flags) &&
+ bitset(MM_CVTMIME, MimeMode)))))
+ {
+ usrerr("554 Cannot send 8-bit data to 7-bit destination");
+ rcode = EX_DATAERR;
+ e->e_status = "5.6.3";
+ goto give_up;
+ }
+
+ if (tTd(62, 8))
+ checkfds("before delivery");
+
+ /* check for Local Person Communication -- not for mortals!!! */
+ if (strcmp(m->m_mailer, "[LPC]") == 0)
+ {
+ mci = (MCI *) xalloc(sizeof *mci);
+ bzero((char *) mci, sizeof *mci);
+ mci->mci_in = stdin;
+ mci->mci_out = stdout;
+ mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN;
+ mci->mci_mailer = m;
+ }
+ else if (strcmp(m->m_mailer, "[IPC]") == 0 ||
+ strcmp(m->m_mailer, "[TCP]") == 0)
+ {
+#if DAEMON
+ register int i;
+
+ if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
+ {
+ syserr("null host name for %s mailer", m->m_mailer);
+ rcode = EX_CONFIG;
+ goto give_up;
+ }
+
+ CurHostName = pv[1];
+ curhost = hostsignature(m, pv[1], e);
+
+ if (curhost == NULL || curhost[0] == '\0')
+ {
+ syserr("null host signature for %s", pv[1]);
+ rcode = EX_CONFIG;
+ goto give_up;
+ }
+
+ if (!clever)
+ {
+ syserr("554 non-clever IPC");
+ rcode = EX_CONFIG;
+ goto give_up;
+ }
+ if (pv[2] != NULL)
+ {
+ port = htons(atoi(pv[2]));
+ if (port == 0)
+ {
+ struct servent *sp = getservbyname(pv[2], "tcp");
+
+ if (sp == NULL)
+ syserr("Service %s unknown", pv[2]);
+ else
+ port = sp->s_port;
+ }
+ }
+tryhost:
+ while (*curhost != '\0')
+ {
+ static char hostbuf[MAXNAME + 1];
+ extern int makeconnection __P((char *, u_short, MCI *, ENVELOPE *));
+
+ /* pull the next host from the signature */
+ p = strchr(curhost, ':');
+ if (p == NULL)
+ p = (char *) &curhost[strlen(curhost)];
+ if (p == curhost)
+ {
+ syserr("deliver: null host name in signature");
+ curhost++;
+ continue;
+ }
+ i = p - curhost;
+ if (i >= sizeof hostbuf)
+ i = sizeof hostbuf - 1;
+ strncpy(hostbuf, curhost, i);
+ hostbuf[i] = '\0';
+ if (*p != '\0')
+ p++;
+ curhost = p;
+
+ /* see if we already know that this host is fried */
+ CurHostName = hostbuf;
+ mci = mci_get(hostbuf, m);
+ if (mci->mci_state != MCIS_CLOSED)
+ {
+ if (tTd(11, 1))
+ {
+ printf("openmailer: ");
+ mci_dump(mci, FALSE);
+ }
+ CurHostName = mci->mci_host;
+ message("Using cached %sSMTP connection to %s via %s...",
+ bitset(MCIF_ESMTP, mci->mci_flags) ? "E" : "",
+ hostbuf, m->m_name);
+ break;
+ }
+ mci->mci_mailer = m;
+ if (mci->mci_exitstat != EX_OK)
+ {
+ if (mci->mci_exitstat == EX_TEMPFAIL)
+ goodmxfound = TRUE;
+ continue;
+ }
+
+ if (mci_lock_host(mci) != EX_OK)
+ {
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL);
+ goodmxfound = TRUE;
+ continue;
+ }
+
+ /* try the connection */
+ setproctitle("%s %s: %s", e->e_id, hostbuf, "user open");
+ if (port == 0)
+ message("Connecting to %s via %s...",
+ hostbuf, m->m_name);
+ else
+ message("Connecting to %s port %d via %s...",
+ hostbuf, ntohs(port), m->m_name);
+ i = makeconnection(hostbuf, port, mci, e);
+ mci->mci_lastuse = curtime();
+ mci->mci_exitstat = i;
+ mci->mci_errno = errno;
+#if NAMED_BIND
+ mci->mci_herrno = h_errno;
+#endif
+ if (i == EX_OK)
+ {
+ goodmxfound = TRUE;
+ mci->mci_state = MCIS_OPENING;
+ mci_cache(mci);
+ if (TrafficLogFile != NULL)
+ fprintf(TrafficLogFile, "%05d === CONNECT %s\n",
+ (int) getpid(), hostbuf);
+ break;
+ }
+ else
+ {
+ if (tTd(11, 1))
+ printf("openmailer: makeconnection => stat=%d, errno=%d\n",
+ i, errno);
+ if (i == EX_TEMPFAIL)
+ goodmxfound = TRUE;
+ mci_unlock_host(mci);
+ }
+
+ /* enter status of this host */
+ setstat(i);
+
+ /* should print some message here for -v mode */
+ }
+ if (mci == NULL)
+ {
+ syserr("deliver: no host name");
+ rcode = EX_SOFTWARE;
+ goto give_up;
+ }
+ mci->mci_pid = 0;
+#else /* no DAEMON */
+ syserr("554 openmailer: no IPC");
+ if (tTd(11, 1))
+ printf("openmailer: NULL\n");
+ rcode = EX_UNAVAILABLE;
+ goto give_up;
+#endif /* DAEMON */
+ }
+ else
+ {
+ /* flush any expired connections */
+ (void) mci_scan(NULL);
+ mci = NULL;
+
+#if SMTP
+ if (bitnset(M_LMTP, m->m_flags))
+ {
+ /* try to get a cached connection */
+ mci = mci_get(m->m_name, m);
+ if (mci->mci_host == NULL)
+ mci->mci_host = m->m_name;
+ CurHostName = mci->mci_host;
+ if (mci->mci_state != MCIS_CLOSED)
+ {
+ message("Using cached LMTP connection for %s...",
+ m->m_name);
+ goto do_transfer;
+ }
+ }
+#endif
+
+ /* announce the connection to verbose listeners */
+ if (host == NULL || host[0] == '\0')
+ message("Connecting to %s...", m->m_name);
+ else
+ message("Connecting to %s via %s...", host, m->m_name);
+ if (TrafficLogFile != NULL)
+ {
+ char **av;
+
+ fprintf(TrafficLogFile, "%05d === EXEC", (int) getpid());
+ for (av = pv; *av != NULL; av++)
+ fprintf(TrafficLogFile, " %s", *av);
+ fprintf(TrafficLogFile, "\n");
+ }
+
+#if XDEBUG
+ checkfd012("before creating mail pipe");
+#endif
+
+ /* create a pipe to shove the mail through */
+ if (pipe(mpvect) < 0)
+ {
+ syserr("%s... openmailer(%s): pipe (to mailer)",
+ shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
+ if (tTd(11, 1))
+ printf("openmailer: NULL\n");
+ rcode = EX_OSERR;
+ goto give_up;
+ }
+
+#if XDEBUG
+ /* make sure we didn't get one of the standard I/O files */
+ if (mpvect[0] < 3 || mpvect[1] < 3)
+ {
+ syserr("%s... openmailer(%s): bogus mpvect %d %d",
+ shortenstring(e->e_to, MAXSHORTSTR), m->m_name,
+ mpvect[0], mpvect[1]);
+ printopenfds(TRUE);
+ if (tTd(11, 1))
+ printf("openmailer: NULL\n");
+ rcode = EX_OSERR;
+ goto give_up;
+ }
+
+ /* make sure system call isn't dead meat */
+ checkfdopen(mpvect[0], "mpvect[0]");
+ checkfdopen(mpvect[1], "mpvect[1]");
+ if (mpvect[0] == mpvect[1] ||
+ (e->e_lockfp != NULL &&
+ (mpvect[0] == fileno(e->e_lockfp) ||
+ mpvect[1] == fileno(e->e_lockfp))))
+ {
+ if (e->e_lockfp == NULL)
+ syserr("%s... openmailer(%s): overlapping mpvect %d %d",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name, mpvect[0], mpvect[1]);
+ else
+ syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name, mpvect[0], mpvect[1],
+ fileno(e->e_lockfp));
+ }
+#endif
+
+ /* if this mailer speaks smtp, create a return pipe */
+#if SMTP
+ if (clever)
+ {
+ if (pipe(rpvect) < 0)
+ {
+ syserr("%s... openmailer(%s): pipe (from mailer)",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name);
+ (void) close(mpvect[0]);
+ (void) close(mpvect[1]);
+ if (tTd(11, 1))
+ printf("openmailer: NULL\n");
+ rcode = EX_OSERR;
+ goto give_up;
+ }
+# if XDEBUG
+ checkfdopen(rpvect[0], "rpvect[0]");
+ checkfdopen(rpvect[1], "rpvect[1]");
+# endif
+ }
+#endif
+
+ /*
+ ** Actually fork the mailer process.
+ ** DOFORK is clever about retrying.
+ **
+ ** Dispose of SIGCHLD signal catchers that may be laying
+ ** around so that endmail will get it.
+ */
+
+ if (e->e_xfp != NULL)
+ (void) fflush(e->e_xfp); /* for debugging */
+ (void) fflush(stdout);
+ (void) setsignal(SIGCHLD, SIG_DFL);
+ DOFORK(FORK);
+ /* pid is set by DOFORK */
+ if (pid < 0)
+ {
+ /* failure */
+ syserr("%s... openmailer(%s): cannot fork",
+ shortenstring(e->e_to, MAXSHORTSTR), m->m_name);
+ (void) close(mpvect[0]);
+ (void) close(mpvect[1]);
+#if SMTP
+ if (clever)
+ {
+ (void) close(rpvect[0]);
+ (void) close(rpvect[1]);
+ }
+#endif
+ if (tTd(11, 1))
+ printf("openmailer: NULL\n");
+ rcode = EX_OSERR;
+ goto give_up;
+ }
+ else if (pid == 0)
+ {
+ int i;
+ int saveerrno;
+ int new_euid = NO_UID;
+ int new_ruid = NO_UID;
+ int new_gid = NO_GID;
+ struct stat stb;
+ extern int DtableSize;
+
+ if (e->e_lockfp != NULL)
+ (void) close(fileno(e->e_lockfp));
+
+ /* child -- set up input & exec mailer */
+ (void) setsignal(SIGINT, SIG_IGN);
+ (void) setsignal(SIGHUP, SIG_IGN);
+ (void) setsignal(SIGTERM, SIG_DFL);
+
+ if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
+ stb.st_mode = 0;
+
+#if HASSETUSERCONTEXT
+ /*
+ ** Set user resources.
+ */
+
+ if (contextaddr != NULL)
+ {
+ struct passwd *pwd;
+
+ if (contextaddr->q_ruser != NULL)
+ pwd = sm_getpwnam(contextaddr->q_ruser);
+ else
+ pwd = sm_getpwnam(contextaddr->q_user);
+ if (pwd != NULL)
+ (void) setusercontext(NULL,
+ pwd, pwd->pw_uid,
+ LOGIN_SETRESOURCES|LOGIN_SETPRIORITY);
+ }
+#endif
+
+ /* tweak niceness */
+ if (m->m_nice != 0)
+ nice(m->m_nice);
+
+ /* reset group id */
+ if (bitnset(M_SPECIFIC_UID, m->m_flags))
+ new_gid = m->m_gid;
+ else if (bitset(S_ISGID, stb.st_mode))
+ new_gid = stb.st_gid;
+ else if (ctladdr != NULL && ctladdr->q_gid != 0)
+ {
+ if (!DontInitGroups)
+ {
+ char *u = ctladdr->q_ruser;
+
+ if (u == NULL)
+ u = ctladdr->q_user;
+
+ if (initgroups(u, ctladdr->q_gid) == -1 && suidwarn)
+ syserr("openmailer: initgroups(%s, %d) failed",
+ u, ctladdr->q_gid);
+ }
+ else
+ {
+ GIDSET_T gidset[1];
+
+ gidset[0] = ctladdr->q_gid;
+ if (setgroups(1, gidset) == -1 && suidwarn)
+ syserr("openmailer: setgroups() failed");
+ }
+ new_gid = ctladdr->q_gid;
+ }
+ else
+ {
+ if (!DontInitGroups)
+ {
+ if (initgroups(DefUser, DefGid) == -1 && suidwarn)
+ syserr("openmailer: initgroups(%s, %d) failed",
+ DefUser, DefGid);
+ }
+ else
+ {
+ GIDSET_T gidset[1];
+
+ gidset[0] = DefGid;
+ if (setgroups(1, gidset) == -1 && suidwarn)
+ syserr("openmailer: setgroups() failed");
+ }
+ if (m->m_gid == 0)
+ new_gid = DefGid;
+ else
+ new_gid = m->m_gid;
+ }
+ if (new_gid != NO_GID && setgid(new_gid) < 0 && suidwarn)
+ syserr("openmailer: setgid(%ld) failed",
+ (long) new_gid);
+
+ /* reset user id */
+ endpwent();
+ if (bitnset(M_SPECIFIC_UID, m->m_flags))
+ new_euid = m->m_uid;
+ else if (bitset(S_ISUID, stb.st_mode))
+ new_ruid = stb.st_uid;
+ else if (ctladdr != NULL && ctladdr->q_uid != 0)
+ new_ruid = ctladdr->q_uid;
+ else if (m->m_uid != 0)
+ new_ruid = m->m_uid;
+ else
+ new_ruid = DefUid;
+ if (new_euid != NO_UID)
+ {
+ vendor_set_uid(new_euid);
+#if USESETEUID
+ if (seteuid(new_euid) < 0 && suidwarn)
+ syserr("openmailer: seteuid(%ld) failed",
+ (long) new_euid);
+#else
+# if HASSETREUID
+ if (setreuid(new_ruid, new_euid) < 0 && suidwarn)
+ syserr("openmailer: setreuid(%ld, %ld) failed",
+ (long) new_ruid, (long) new_euid);
+# else
+ if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn)
+ syserr("openmailer: setuid(%ld) failed",
+ (long) new_euid);
+# endif
+#endif
+ }
+ else if (new_ruid != NO_UID)
+ {
+ vendor_set_uid(new_ruid);
+ if (setuid(new_ruid) < 0 && suidwarn)
+ syserr("openmailer: setuid(%ld) failed",
+ (long) new_ruid);
+ }
+
+ if (tTd(11, 2))
+ printf("openmailer: running as r/euid=%d/%d\n",
+ (int) getuid(), (int) geteuid());
+
+ /* move into some "safe" directory */
+ if (m->m_execdir != NULL)
+ {
+ char *q;
+ char buf[MAXLINE + 1];
+
+ for (p = m->m_execdir; p != NULL; p = q)
+ {
+ q = strchr(p, ':');
+ if (q != NULL)
+ *q = '\0';
+ expand(p, buf, sizeof buf, e);
+ if (q != NULL)
+ *q++ = ':';
+ if (tTd(11, 20))
+ printf("openmailer: trydir %s\n",
+ buf);
+ if (buf[0] != '\0' && chdir(buf) >= 0)
+ break;
+ }
+ }
+
+ /* arrange to filter std & diag output of command */
+#if SMTP
+ if (clever)
+ {
+ (void) close(rpvect[0]);
+ if (dup2(rpvect[1], STDOUT_FILENO) < 0)
+ {
+ syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name, rpvect[1]);
+ _exit(EX_OSERR);
+ }
+ (void) close(rpvect[1]);
+ }
+ else
+ {
+ /* put mailer output in transcript */
+ if (dup2(fileno(e->e_xfp), STDOUT_FILENO) < 0)
+ {
+ syserr("%s... openmailer(%s): cannot dup xscript %d for stdout",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name, fileno(e->e_xfp));
+ _exit(EX_OSERR);
+ }
+ }
+#endif
+ if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
+ {
+ syserr("%s... openmailer(%s): cannot dup stdout for stderr",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name);
+ _exit(EX_OSERR);
+ }
+
+ /* arrange to get standard input */
+ (void) close(mpvect[1]);
+ if (dup2(mpvect[0], STDIN_FILENO) < 0)
+ {
+ syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
+ shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name, mpvect[0]);
+ _exit(EX_OSERR);
+ }
+ (void) close(mpvect[0]);
+
+ /* arrange for all the files to be closed */
+ for (i = 3; i < DtableSize; i++)
+ {
+ register int j;
+
+ if ((j = fcntl(i, F_GETFD, 0)) != -1)
+ (void) fcntl(i, F_SETFD, j | 1);
+ }
+
+ /* run disconnected from terminal */
+ (void) setsid();
+
+ /* try to execute the mailer */
+ execve(m->m_mailer, (ARGV_T) pv, (ARGV_T) UserEnviron);
+ saveerrno = errno;
+ syserr("Cannot exec %s", m->m_mailer);
+ if (bitnset(M_LOCALMAILER, m->m_flags) ||
+ transienterror(saveerrno))
+ _exit(EX_OSERR);
+ _exit(EX_UNAVAILABLE);
+ }
+
+ /*
+ ** Set up return value.
+ */
+
+ if (mci == NULL)
+ {
+ mci = (MCI *) xalloc(sizeof *mci);
+ bzero((char *) mci, sizeof *mci);
+ }
+ mci->mci_mailer = m;
+ if (clever)
+ {
+ mci->mci_state = MCIS_OPENING;
+ mci_cache(mci);
+ }
+ else
+ {
+ mci->mci_state = MCIS_OPEN;
+ }
+ mci->mci_pid = pid;
+ (void) close(mpvect[0]);
+ mci->mci_out = fdopen(mpvect[1], "w");
+ if (mci->mci_out == NULL)
+ {
+ syserr("deliver: cannot create mailer output channel, fd=%d",
+ mpvect[1]);
+ (void) close(mpvect[1]);
+#if SMTP
+ if (clever)
+ {
+ (void) close(rpvect[0]);
+ (void) close(rpvect[1]);
+ }
+#endif
+ rcode = EX_OSERR;
+ goto give_up;
+ }
+#if SMTP
+ if (clever)
+ {
+ (void) close(rpvect[1]);
+ mci->mci_in = fdopen(rpvect[0], "r");
+ if (mci->mci_in == NULL)
+ {
+ syserr("deliver: cannot create mailer input channel, fd=%d",
+ mpvect[1]);
+ (void) close(rpvect[0]);
+ fclose(mci->mci_out);
+ mci->mci_out = NULL;
+ rcode = EX_OSERR;
+ goto give_up;
+ }
+ }
+ else
+#endif
+ {
+ mci->mci_flags |= MCIF_TEMP;
+ mci->mci_in = NULL;
+ }
+ }
+
+ /*
+ ** If we are in SMTP opening state, send initial protocol.
+ */
+
+ if (bitnset(M_7BITS, m->m_flags) &&
+ (!clever || mci->mci_state == MCIS_OPENING))
+ mci->mci_flags |= MCIF_7BIT;
+#if SMTP
+ if (clever && mci->mci_state != MCIS_CLOSED)
+ {
+ extern void smtpinit __P((MAILER *, MCI *, ENVELOPE *));
+
+ smtpinit(m, mci, e);
+ }
+#endif
+
+do_transfer:
+ /* clear out per-message flags from connection structure */
+ mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
+
+ if (bitset(EF_HAS8BIT, e->e_flags) &&
+ !bitset(EF_DONT_MIME, e->e_flags) &&
+ bitnset(M_7BITS, m->m_flags))
+ mci->mci_flags |= MCIF_CVT8TO7;
+
+#if MIME7TO8
+ if (bitnset(M_MAKE8BIT, m->m_flags) &&
+ !bitset(MCIF_7BIT, mci->mci_flags) &&
+ (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
+ (strcasecmp(p, "quoted-printable") == 0 ||
+ strcasecmp(p, "base64") == 0) &&
+ (p = hvalue("Content-Type", e->e_header)) != NULL)
+ {
+ /* may want to convert 7 -> 8 */
+ /* XXX should really parse it here -- and use a class XXX */
+ if (strncasecmp(p, "text/plain", 10) == 0 &&
+ (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
+ mci->mci_flags |= MCIF_CVT7TO8;
+ }
+#endif
+
+ if (tTd(11, 1))
+ {
+ printf("openmailer: ");
+ mci_dump(mci, FALSE);
+ }
+
+ if (mci->mci_state != MCIS_OPEN)
+ {
+ /* couldn't open the mailer */
+ rcode = mci->mci_exitstat;
+ errno = mci->mci_errno;
+#if NAMED_BIND
+ h_errno = mci->mci_herrno;
+#endif
+ if (rcode == EX_OK)
+ {
+ /* shouldn't happen */
+ syserr("554 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s",
+ (long) mci, rcode, errno, mci->mci_state,
+ firstsig);
+ mci_dump_all(TRUE);
+ rcode = EX_SOFTWARE;
+ }
+#if DAEMON
+ else if (curhost != NULL && *curhost != '\0')
+ {
+ /* try next MX site */
+ goto tryhost;
+ }
+#endif
+ }
+ else if (!clever)
+ {
+ /*
+ ** Format and send message.
+ */
+
+ mci->mci_contentlen = 0;
+ putfromline(mci, e);
+ (*e->e_puthdr)(mci, e->e_header, e);
+ (*e->e_putbody)(mci, e, NULL);
+
+ /* get the exit status */
+ rcode = endmailer(mci, e, pv);
+ }
+ else
+#if SMTP
+ {
+ extern int smtpmailfrom __P((MAILER *, MCI *, ENVELOPE *));
+ extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
+ extern int smtpdata __P((MAILER *, MCI *, ENVELOPE *));
+
+ /*
+ ** Send the MAIL FROM: protocol
+ */
+
+ rcode = smtpmailfrom(m, mci, e);
+ if (rcode == EX_OK)
+ {
+ register char *t = tobuf;
+ register int i;
+
+ /* send the recipient list */
+ tobuf[0] = '\0';
+ for (to = tochain; to != NULL; to = to->q_tchain)
+ {
+ e->e_to = to->q_paddr;
+ if (strlen(to->q_paddr) + (t - tobuf) + 2 > sizeof tobuf)
+ {
+ /* not enough room */
+ continue;
+ }
+ else if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
+ {
+ markfailure(e, to, mci, i);
+ giveresponse(i, m, mci, ctladdr, xstart, e);
+ }
+ else
+ {
+ *t++ = ',';
+ for (p = to->q_paddr; *p; *t++ = *p++)
+ continue;
+ *t = '\0';
+ }
+ }
+
+ /* now send the data */
+ if (tobuf[0] == '\0')
+ {
+ rcode = EX_OK;
+ e->e_to = NULL;
+ if (bitset(MCIF_CACHED, mci->mci_flags))
+ smtprset(m, mci, e);
+ }
+ else
+ {
+ e->e_to = tobuf + 1;
+ rcode = smtpdata(m, mci, e);
+ }
+ }
+# if DAEMON
+ if (rcode == EX_TEMPFAIL && curhost != NULL && *curhost != '\0')
+ {
+ /* try next MX site */
+ goto tryhost;
+ }
+# endif
+ }
+#else /* not SMTP */
+ {
+ syserr("554 deliver: need SMTP compiled to use clever mailer");
+ rcode = EX_CONFIG;
+ goto give_up;
+ }
+#endif /* SMTP */
+#if NAMED_BIND
+ if (ConfigLevel < 2)
+ _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */
+#endif
+
+ if (tTd(62, 1))
+ checkfds("after delivery");
+
+ /*
+ ** Do final status disposal.
+ ** We check for something in tobuf for the SMTP case.
+ ** If we got a temporary failure, arrange to queue the
+ ** addressees.
+ */
+
+ give_up:
+#if SMTP
+ if (bitnset(M_LMTP, m->m_flags))
+ {
+ lmtp_rcode = rcode;
+ tobuf[0] = '\0';
+ anyok = FALSE;
+ }
+ else
+#endif
+ anyok = rcode == EX_OK;
+
+ for (to = tochain; to != NULL; to = to->q_tchain)
+ {
+ /* see if address already marked */
+ if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
+ continue;
+
+#if SMTP
+ /* if running LMTP, get the status for each address */
+ if (bitnset(M_LMTP, m->m_flags))
+ {
+ extern int smtpgetstat __P((MAILER *, MCI *, ENVELOPE *));
+
+ if (lmtp_rcode == EX_OK)
+ rcode = smtpgetstat(m, mci, e);
+ if (rcode == EX_OK)
+ {
+ if (strlen(to->q_paddr) + strlen(tobuf) + 2 >= sizeof tobuf)
+ {
+ syserr("LMTP tobuf overflow");
+ }
+ else
+ {
+ strcat(tobuf, ",");
+ strcat(tobuf, to->q_paddr);
+ }
+ anyok = TRUE;
+ }
+ else
+ {
+ e->e_to = to->q_paddr;
+ markfailure(e, to, mci, rcode);
+ giveresponse(rcode, m, mci, ctladdr, xstart, e);
+ e->e_to = tobuf + 1;
+ continue;
+ }
+ }
+ else
+#endif
+ {
+ /* mark bad addresses */
+ if (rcode != EX_OK)
+ {
+ if (goodmxfound && rcode == EX_NOHOST)
+ rcode = EX_TEMPFAIL;
+ markfailure(e, to, mci, rcode);
+ continue;
+ }
+ }
+
+ /* successful delivery */
+ to->q_flags |= QSENT;
+ to->q_statdate = curtime();
+ e->e_nsent++;
+ if (bitnset(M_LOCALMAILER, m->m_flags) &&
+ bitset(QPINGONSUCCESS, to->q_flags))
+ {
+ to->q_flags |= QDELIVERED;
+ to->q_status = "2.1.5";
+ fprintf(e->e_xfp, "%s... Successfully delivered\n",
+ to->q_paddr);
+ }
+ else if (bitset(QPINGONSUCCESS, to->q_flags) &&
+ bitset(QPRIMARY, to->q_flags) &&
+ !bitset(MCIF_DSN, mci->mci_flags))
+ {
+ to->q_flags |= QRELAYED;
+ fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n",
+ to->q_paddr);
+ }
+ }
+
+#if SMTP
+ if (bitnset(M_LMTP, m->m_flags))
+ {
+ /*
+ ** Global information applies to the last recipient only;
+ ** clear it out to avoid bogus errors.
+ */
+
+ rcode = EX_OK;
+ e->e_statmsg = NULL;
+
+ /* reset the mci state for the next transaction */
+ if (mci != NULL && mci->mci_state == MCIS_ACTIVE)
+ mci->mci_state = MCIS_OPEN;
+ }
+#endif
+
+ if (tobuf[0] != '\0')
+ giveresponse(rcode, m, mci, ctladdr, xstart, e);
+ if (anyok)
+ markstats(e, tochain, FALSE);
+ mci_store_persistent(mci);
+
+#if SMTP
+ /* now close the connection */
+ if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED &&
+ !bitset(MCIF_CACHED, mci->mci_flags))
+ smtpquit(m, mci, e);
+#endif
+
+ /*
+ ** Restore state and return.
+ */
+
+#if XDEBUG
+ {
+ char wbuf[MAXLINE];
+
+ /* make absolutely certain 0, 1, and 2 are in use */
+ snprintf(wbuf, sizeof wbuf, "%s... end of deliver(%s)",
+ e->e_to == NULL ? "NO-TO-LIST"
+ : shortenstring(e->e_to, MAXSHORTSTR),
+ m->m_name);
+ checkfd012(wbuf);
+ }
+#endif
+
+ errno = 0;
+ define('g', (char *) NULL, e);
+ return (rcode);
+}
+ /*
+** MARKFAILURE -- mark a failure on a specific address.
+**
+** Parameters:
+** e -- the envelope we are sending.
+** q -- the address to mark.
+** mci -- mailer connection information.
+** rcode -- the code signifying the particular failure.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** marks the address (and possibly the envelope) with the
+** failure so that an error will be returned or
+** the message will be queued, as appropriate.
+*/
+
+void
+markfailure(e, q, mci, rcode)
+ register ENVELOPE *e;
+ register ADDRESS *q;
+ register MCI *mci;
+ int rcode;
+{
+ char *stat = NULL;
+
+ switch (rcode)
+ {
+ case EX_OK:
+ break;
+
+ case EX_TEMPFAIL:
+ case EX_IOERR:
+ case EX_OSERR:
+ q->q_flags |= QQUEUEUP;
+ q->q_flags &= ~QDONTSEND;
+ break;
+
+ default:
+ q->q_flags |= QBADADDR;
+ break;
+ }
+
+ /* find most specific error code possible */
+ if (mci != NULL && mci->mci_status != NULL)
+ {
+ q->q_status = mci->mci_status;
+ if (mci->mci_rstatus != NULL)
+ q->q_rstatus = newstr(mci->mci_rstatus);
+ else
+ q->q_rstatus = NULL;
+ }
+ else if (e->e_status != NULL)
+ {
+ q->q_status = e->e_status;
+ q->q_rstatus = NULL;
+ }
+ else
+ {
+ switch (rcode)
+ {
+ case EX_USAGE:
+ stat = "5.5.4";
+ break;
+
+ case EX_DATAERR:
+ stat = "5.5.2";
+ break;
+
+ case EX_NOUSER:
+ stat = "5.1.1";
+ break;
+
+ case EX_NOHOST:
+ stat = "5.1.2";
+ break;
+
+ case EX_NOINPUT:
+ case EX_CANTCREAT:
+ case EX_NOPERM:
+ stat = "5.3.0";
+ break;
+
+ case EX_UNAVAILABLE:
+ case EX_SOFTWARE:
+ case EX_OSFILE:
+ case EX_PROTOCOL:
+ case EX_CONFIG:
+ stat = "5.5.0";
+ break;
+
+ case EX_OSERR:
+ case EX_IOERR:
+ stat = "4.5.0";
+ break;
+
+ case EX_TEMPFAIL:
+ stat = "4.2.0";
+ break;
+ }
+ if (stat != NULL)
+ q->q_status = stat;
+ }
+
+ q->q_statdate = curtime();
+ if (CurHostName != NULL && CurHostName[0] != '\0')
+ q->q_statmta = newstr(CurHostName);
+ if (rcode != EX_OK && q->q_rstatus == NULL &&
+ q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL &&
+ strcasecmp(q->q_mailer->m_diagtype, "UNIX") == 0)
+ {
+ char buf[30];
+
+ (void) snprintf(buf, sizeof buf, "%d", rcode);
+ q->q_rstatus = newstr(buf);
+ }
+}
+ /*
+** ENDMAILER -- Wait for mailer to terminate.
+**
+** We should never get fatal errors (e.g., segmentation
+** violation), so we report those specially. For other
+** errors, we choose a status message (into statmsg),
+** and if it represents an error, we print it.
+**
+** Parameters:
+** pid -- pid of mailer.
+** e -- the current envelope.
+** pv -- the parameter vector that invoked the mailer
+** (for error messages).
+**
+** Returns:
+** exit code of mailer.
+**
+** Side Effects:
+** none.
+*/
+
+int
+endmailer(mci, e, pv)
+ register MCI *mci;
+ register ENVELOPE *e;
+ char **pv;
+{
+ int st;
+
+ mci_unlock_host(mci);
+
+ /* close any connections */
+ if (mci->mci_in != NULL)
+ (void) xfclose(mci->mci_in, mci->mci_mailer->m_name, "mci_in");
+ if (mci->mci_out != NULL)
+ (void) xfclose(mci->mci_out, mci->mci_mailer->m_name, "mci_out");
+ mci->mci_in = mci->mci_out = NULL;
+ mci->mci_state = MCIS_CLOSED;
+
+ /* in the IPC case there is nothing to wait for */
+ if (mci->mci_pid == 0)
+ return (EX_OK);
+
+#if _FFR_TIMEOUT_WAIT
+ put a timeout around the wait
+#endif
+
+ /* wait for the mailer process to die and collect status */
+ st = waitfor(mci->mci_pid);
+ if (st == -1)
+ {
+ syserr("endmailer %s: wait", mci->mci_mailer->m_name);
+ return (EX_SOFTWARE);
+ }
+
+ if (WIFEXITED(st))
+ {
+ /* normal death -- return status */
+ return (WEXITSTATUS(st));
+ }
+
+ /* it died a horrid death */
+ syserr("451 mailer %s died with signal %o",
+ mci->mci_mailer->m_name, st);
+
+ /* log the arguments */
+ if (pv != NULL && e->e_xfp != NULL)
+ {
+ register char **av;
+
+ fprintf(e->e_xfp, "Arguments:");
+ for (av = pv; *av != NULL; av++)
+ fprintf(e->e_xfp, " %s", *av);
+ fprintf(e->e_xfp, "\n");
+ }
+
+ ExitStat = EX_TEMPFAIL;
+ return (EX_TEMPFAIL);
+}
+ /*
+** GIVERESPONSE -- Interpret an error response from a mailer
+**
+** Parameters:
+** stat -- the status code from the mailer (high byte
+** only; core dumps must have been taken care of
+** already).
+** m -- the mailer info for this mailer.
+** mci -- the mailer connection info -- can be NULL if the
+** response is given before the connection is made.
+** ctladdr -- the controlling address for the recipient
+** address(es).
+** xstart -- the transaction start time, for computing
+** transaction delays.
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Errors may be incremented.
+** ExitStat may be set.
+*/
+
+void
+giveresponse(stat, m, mci, ctladdr, xstart, e)
+ int stat;
+ register MAILER *m;
+ register MCI *mci;
+ ADDRESS *ctladdr;
+ time_t xstart;
+ ENVELOPE *e;
+{
+ register const char *statmsg;
+ extern char *SysExMsg[];
+ register int i;
+ extern int N_SysEx;
+ char buf[MAXLINE];
+
+ if (e == NULL)
+ syserr("giveresponse: null envelope");
+
+ /*
+ ** Compute status message from code.
+ */
+
+ i = stat - EX__BASE;
+ if (stat == 0)
+ {
+ statmsg = "250 Sent";
+ if (e->e_statmsg != NULL)
+ {
+ (void) snprintf(buf, sizeof buf, "%s (%s)",
+ statmsg, shortenstring(e->e_statmsg, 403));
+ statmsg = buf;
+ }
+ }
+ else if (i < 0 || i >= N_SysEx)
+ {
+ (void) snprintf(buf, sizeof buf, "554 unknown mailer error %d",
+ stat);
+ stat = EX_UNAVAILABLE;
+ statmsg = buf;
+ }
+ else if (stat == EX_TEMPFAIL)
+ {
+ char *bp = buf;
+
+ snprintf(bp, SPACELEFT(buf, bp), "%s", SysExMsg[i] + 1);
+ bp += strlen(bp);
+#if NAMED_BIND
+ if (h_errno == TRY_AGAIN)
+ statmsg = errstring(h_errno+E_DNSBASE);
+ else
+#endif
+ {
+ if (errno != 0)
+ statmsg = errstring(errno);
+ else
+ {
+#if SMTP
+ statmsg = SmtpError;
+#else /* SMTP */
+ statmsg = NULL;
+#endif /* SMTP */
+ }
+ }
+ if (statmsg != NULL && statmsg[0] != '\0')
+ snprintf(bp, SPACELEFT(buf, bp), ": %s", statmsg);
+ statmsg = buf;
+ }
+#if NAMED_BIND
+ else if (stat == EX_NOHOST && h_errno != 0)
+ {
+ statmsg = errstring(h_errno + E_DNSBASE);
+ (void) snprintf(buf, sizeof buf, "%s (%s)",
+ SysExMsg[i] + 1, statmsg);
+ statmsg = buf;
+ }
+#endif
+ else
+ {
+ statmsg = SysExMsg[i];
+ if (*statmsg++ == ':' && errno != 0)
+ {
+ (void) snprintf(buf, sizeof buf, "%s: %s",
+ statmsg, errstring(errno));
+ statmsg = buf;
+ }
+ }
+
+ /*
+ ** Print the message as appropriate
+ */
+
+ if (stat == EX_OK || stat == EX_TEMPFAIL)
+ {
+ extern char MsgBuf[];
+
+ message("%s", &statmsg[4]);
+ if (stat == EX_TEMPFAIL && e->e_xfp != NULL)
+ fprintf(e->e_xfp, "%s\n", &MsgBuf[4]);
+ }
+ else
+ {
+ char mbuf[8];
+
+ Errors++;
+ snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg);
+ usrerr(mbuf, &statmsg[4]);
+ }
+
+ /*
+ ** Final cleanup.
+ ** Log a record of the transaction. Compute the new
+ ** ExitStat -- if we already had an error, stick with
+ ** that.
+ */
+
+ if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
+ LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6))
+ logdelivery(m, mci, &statmsg[4], ctladdr, xstart, e);
+
+ if (tTd(11, 2))
+ printf("giveresponse: stat=%d, e->e_message=%s\n",
+ stat, e->e_message == NULL ? "<NULL>" : e->e_message);
+
+ if (stat != EX_TEMPFAIL)
+ setstat(stat);
+ if (stat != EX_OK && (stat != EX_TEMPFAIL || e->e_message == NULL))
+ {
+ if (e->e_message != NULL)
+ free(e->e_message);
+ e->e_message = newstr(&statmsg[4]);
+ }
+ errno = 0;
+#if NAMED_BIND
+ h_errno = 0;
+#endif
+}
+ /*
+** LOGDELIVERY -- log the delivery in the system log
+**
+** Care is taken to avoid logging lines that are too long, because
+** some versions of syslog have an unfortunate proclivity for core
+** dumping. This is a hack, to be sure, that is at best empirical.
+**
+** Parameters:
+** m -- the mailer info. Can be NULL for initial queue.
+** mci -- the mailer connection info -- can be NULL if the
+** log is occuring when no connection is active.
+** stat -- the message to print for the status.
+** ctladdr -- the controlling address for the to list.
+** xstart -- the transaction start time, used for
+** computing transaction delay.
+** e -- the current envelope.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** none
+*/
+
+void
+logdelivery(m, mci, stat, ctladdr, xstart, e)
+ MAILER *m;
+ register MCI *mci;
+ const char *stat;
+ ADDRESS *ctladdr;
+ time_t xstart;
+ register ENVELOPE *e;
+{
+ register char *bp;
+ register char *p;
+ int l;
+ char buf[1024];
+
+# if (SYSLOG_BUFSIZE) >= 256
+ /* ctladdr: max 106 bytes */
+ bp = buf;
+ if (ctladdr != NULL)
+ {
+ snprintf(bp, SPACELEFT(buf, bp), ", ctladdr=%s",
+ shortenstring(ctladdr->q_paddr, 83));
+ bp += strlen(bp);
+ if (bitset(QGOODUID, ctladdr->q_flags))
+ {
+ (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
+ ctladdr->q_uid, ctladdr->q_gid);
+ bp += strlen(bp);
+ }
+ }
+
+ /* delay & xdelay: max 41 bytes */
+ snprintf(bp, SPACELEFT(buf, bp), ", delay=%s",
+ pintvl(curtime() - e->e_ctime, TRUE));
+ bp += strlen(bp);
+
+ if (xstart != (time_t) 0)
+ {
+ snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
+ pintvl(curtime() - xstart, TRUE));
+ bp += strlen(bp);
+ }
+
+ /* mailer: assume about 19 bytes (max 10 byte mailer name) */
+ if (m != NULL)
+ {
+ snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name);
+ bp += strlen(bp);
+ }
+
+ /* relay: max 66 bytes for IPv4 addresses */
+ if (mci != NULL && mci->mci_host != NULL)
+ {
+# if DAEMON
+ extern SOCKADDR CurHostAddr;
+# endif
+
+ snprintf(bp, SPACELEFT(buf, bp), ", relay=%s",
+ shortenstring(mci->mci_host, 40));
+ bp += strlen(bp);
+
+# if DAEMON
+ if (CurHostAddr.sa.sa_family != 0)
+ {
+ snprintf(bp, SPACELEFT(buf, bp), " [%s]",
+ anynet_ntoa(&CurHostAddr));
+ }
+# endif
+ }
+ else if (strcmp(stat, "queued") != 0)
+ {
+ p = macvalue('h', e);
+ if (p != NULL && p[0] != '\0')
+ {
+ snprintf(bp, SPACELEFT(buf, bp), ", relay=%s",
+ shortenstring(p, 40));
+ }
+ }
+ bp += strlen(bp);
+
+#define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4)
+#if (STATLEN) < 63
+# undef STATLEN
+# define STATLEN 63
+#endif
+#if (STATLEN) > 203
+# undef STATLEN
+# define STATLEN 203
+#endif
+
+ /* stat: max 210 bytes */
+ if ((bp - buf) > (sizeof buf - ((STATLEN) + 20)))
+ {
+ /* desperation move -- truncate data */
+ bp = buf + sizeof buf - ((STATLEN) + 17);
+ strcpy(bp, "...");
+ bp += 3;
+ }
+
+ (void) strcpy(bp, ", stat=");
+ bp += strlen(bp);
+
+ (void) strcpy(bp, shortenstring(stat, (STATLEN)));
+
+ /* id, to: max 13 + TOBUFSIZE bytes */
+ l = SYSLOG_BUFSIZE - 100 - strlen(buf);
+ p = e->e_to;
+ while (strlen(p) >= (SIZE_T) l)
+ {
+ register char *q = strchr(p + l, ',');
+
+ if (q == NULL)
+ break;
+ sm_syslog(LOG_INFO, e->e_id,
+ "to=%.*s [more]%s",
+ ++q - p, p, buf);
+ p = q;
+ }
+ sm_syslog(LOG_INFO, e->e_id, "to=%s%s", p, buf);
+
+# else /* we have a very short log buffer size */
+
+ l = SYSLOG_BUFSIZE - 85;
+ p = e->e_to;
+ while (strlen(p) >= (SIZE_T) l)
+ {
+ register char *q = strchr(p + l, ',');
+
+ if (q == NULL)
+ break;
+ sm_syslog(LOG_INFO, e->e_id,
+ "to=%.*s [more]",
+ ++q - p, p);
+ p = q;
+ }
+ sm_syslog(LOG_INFO, e->e_id, "to=%s", p);
+
+ if (ctladdr != NULL)
+ {
+ bp = buf;
+ snprintf(bp, SPACELEFT(buf, bp), "ctladdr=%s",
+ shortenstring(ctladdr->q_paddr, 83));
+ bp += strlen(bp);
+ if (bitset(QGOODUID, ctladdr->q_flags))
+ {
+ (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)",
+ ctladdr->q_uid, ctladdr->q_gid);
+ bp += strlen(bp);
+ }
+ sm_syslog(LOG_INFO, e->e_id, "%s", buf);
+ }
+ bp = buf;
+ snprintf(bp, SPACELEFT(buf, bp), "delay=%s",
+ pintvl(curtime() - e->e_ctime, TRUE));
+ bp += strlen(bp);
+ if (xstart != (time_t) 0)
+ {
+ snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s",
+ pintvl(curtime() - xstart, TRUE));
+ bp += strlen(bp);
+ }
+
+ if (m != NULL)
+ {
+ snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name);
+ bp += strlen(bp);
+ }
+ sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
+
+ buf[0] = '\0';
+ bp = buf;
+ if (mci != NULL && mci->mci_host != NULL)
+ {
+# if DAEMON
+ extern SOCKADDR CurHostAddr;
+# endif
+
+ snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", mci->mci_host);
+ bp += strlen(bp);
+
+# if DAEMON
+ if (CurHostAddr.sa.sa_family != 0)
+ snprintf(bp, SPACELEFT(buf, bp), " [%.100s]",
+ anynet_ntoa(&CurHostAddr));
+# endif
+ }
+ else if (strcmp(stat, "queued") != 0)
+ {
+ p = macvalue('h', e);
+ if (p != NULL && p[0] != '\0')
+ snprintf(buf, sizeof buf, "relay=%.100s", p);
+ }
+ if (buf[0] != '\0')
+ sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf);
+
+ sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(stat, 63));
+# endif /* short log buffer */
+}
+ /*
+** PUTFROMLINE -- output a UNIX-style from line (or whatever)
+**
+** This can be made an arbitrary message separator by changing $l
+**
+** One of the ugliest hacks seen by human eyes is contained herein:
+** UUCP wants those stupid "remote from <host>" lines. Why oh why
+** does a well-meaning programmer such as myself have to deal with
+** this kind of antique garbage????
+**
+** Parameters:
+** mci -- the connection information.
+** e -- the envelope.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** outputs some text to fp.
+*/
+
+void
+putfromline(mci, e)
+ register MCI *mci;
+ ENVELOPE *e;
+{
+ char *template = UnixFromLine;
+ char buf[MAXLINE];
+ char xbuf[MAXLINE];
+
+ if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
+ return;
+
+ mci->mci_flags |= MCIF_INHEADER;
+
+ if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
+ {
+ char *bang;
+
+ expand("\201g", buf, sizeof buf, e);
+ bang = strchr(buf, '!');
+ if (bang == NULL)
+ {
+ char *at;
+ char hname[MAXNAME];
+
+ /*
+ ** If we can construct a UUCP path, do so
+ */
+
+ at = strrchr(buf, '@');
+ if (at == NULL)
+ {
+ expand( "\201k", hname, sizeof hname, e);
+ at = hname;
+ }
+ else
+ *at++ = '\0';
+ (void) snprintf(xbuf, sizeof xbuf,
+ "From %.800s \201d remote from %.100s\n",
+ buf, at);
+ }
+ else
+ {
+ *bang++ = '\0';
+ (void) snprintf(xbuf, sizeof xbuf,
+ "From %.800s \201d remote from %.100s\n",
+ bang, buf);
+ template = xbuf;
+ }
+ }
+ expand(template, buf, sizeof buf, e);
+ putxline(buf, strlen(buf), mci, PXLF_HEADER);
+}
+ /*
+** PUTBODY -- put the body of a message.
+**
+** Parameters:
+** mci -- the connection information.
+** e -- the envelope to put out.
+** separator -- if non-NULL, a message separator that must
+** not be permitted in the resulting message.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** The message is written onto fp.
+*/
+
+/* values for output state variable */
+#define OS_HEAD 0 /* at beginning of line */
+#define OS_CR 1 /* read a carriage return */
+#define OS_INLINE 2 /* putting rest of line */
+
+void
+putbody(mci, e, separator)
+ register MCI *mci;
+ register ENVELOPE *e;
+ char *separator;
+{
+ char buf[MAXLINE];
+
+ /*
+ ** Output the body of the message
+ */
+
+ if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
+ {
+ char *df = queuename(e, 'd');
+
+ e->e_dfp = fopen(df, "r");
+ if (e->e_dfp == NULL)
+ syserr("putbody: Cannot open %s for %s from %s",
+ df, e->e_to, e->e_from.q_paddr);
+ }
+ if (e->e_dfp == NULL)
+ {
+ if (bitset(MCIF_INHEADER, mci->mci_flags))
+ {
+ putline("", mci);
+ mci->mci_flags &= ~MCIF_INHEADER;
+ }
+ putline("<<< No Message Collected >>>", mci);
+ goto endofmessage;
+ }
+ if (e->e_dfino == (ino_t) 0)
+ {
+ struct stat stbuf;
+
+ if (fstat(fileno(e->e_dfp), &stbuf) < 0)
+ e->e_dfino = -1;
+ else
+ {
+ e->e_dfdev = stbuf.st_dev;
+ e->e_dfino = stbuf.st_ino;
+ }
+ }
+ rewind(e->e_dfp);
+
+#if MIME8TO7
+ if (bitset(MCIF_CVT8TO7, mci->mci_flags))
+ {
+ char *boundaries[MAXMIMENESTING + 1];
+
+ /*
+ ** Do 8 to 7 bit MIME conversion.
+ */
+
+ /* make sure it looks like a MIME message */
+ if (hvalue("MIME-Version", e->e_header) == NULL)
+ putline("MIME-Version: 1.0", mci);
+
+ if (hvalue("Content-Type", e->e_header) == NULL)
+ {
+ snprintf(buf, sizeof buf,
+ "Content-Type: text/plain; charset=%s",
+ defcharset(e));
+ putline(buf, mci);
+ }
+
+ /* now do the hard work */
+ boundaries[0] = NULL;
+ mci->mci_flags |= MCIF_INHEADER;
+ mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
+ }
+# if MIME7TO8
+ else if (bitset(MCIF_CVT7TO8, mci->mci_flags))
+ {
+ mime7to8(mci, e->e_header, e);
+ }
+# endif
+ else
+#endif
+ {
+ int ostate;
+ register char *bp;
+ register char *pbp;
+ register int c;
+ register char *xp;
+ int padc;
+ char *buflim;
+ int pos = 0;
+ size_t eol_len;
+ char peekbuf[10];
+
+ /* we can pass it through unmodified */
+ if (bitset(MCIF_INHEADER, mci->mci_flags))
+ {
+ putline("", mci);
+ mci->mci_flags &= ~MCIF_INHEADER;
+ }
+
+ /* determine end of buffer; allow for short mailer lines */
+ buflim = &buf[sizeof buf - 1];
+ if (mci->mci_mailer->m_linelimit > 0 &&
+ mci->mci_mailer->m_linelimit < sizeof buf - 1)
+ buflim = &buf[mci->mci_mailer->m_linelimit - 1];
+ eol_len = strlen(mci->mci_mailer->m_eol);
+
+ /* copy temp file to output with mapping */
+ ostate = OS_HEAD;
+ bp = buf;
+ pbp = peekbuf;
+ while (!ferror(mci->mci_out))
+ {
+ if (pbp > peekbuf)
+ c = *--pbp;
+ else if ((c = getc(e->e_dfp)) == EOF)
+ break;
+ if (bitset(MCIF_7BIT, mci->mci_flags))
+ c &= 0x7f;
+ switch (ostate)
+ {
+ case OS_HEAD:
+#if _FFR_NONULLS
+ if (c == '\0' &&
+ bitnset(M_NONULLS, mci->mci_mailer->m_flags))
+ break;
+#endif
+ if (c != '\r' && c != '\n' && bp < buflim)
+ {
+ *bp++ = c;
+ break;
+ }
+
+ /* check beginning of line for special cases */
+ *bp = '\0';
+ pos = 0;
+ padc = EOF;
+ if (buf[0] == 'F' &&
+ bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
+ strncmp(buf, "From ", 5) == 0)
+ {
+ padc = '>';
+ }
+ if (buf[0] == '-' && buf[1] == '-' &&
+ separator != NULL)
+ {
+ /* possible separator */
+ int sl = strlen(separator);
+
+ if (strncmp(&buf[2], separator, sl) == 0)
+ padc = ' ';
+ }
+ if (buf[0] == '.' &&
+ bitnset(M_XDOT, mci->mci_mailer->m_flags))
+ {
+ padc = '.';
+ }
+
+ /* now copy out saved line */
+ if (TrafficLogFile != NULL)
+ {
+ fprintf(TrafficLogFile, "%05d >>> ",
+ (int) getpid());
+ if (padc != EOF)
+ putc(padc, TrafficLogFile);
+ for (xp = buf; xp < bp; xp++)
+ putc(*xp, TrafficLogFile);
+ if (c == '\n')
+ fputs(mci->mci_mailer->m_eol,
+ TrafficLogFile);
+ }
+ if (padc != EOF)
+ {
+ putc(padc, mci->mci_out);
+ mci->mci_contentlen++;
+ pos++;
+ }
+ for (xp = buf; xp < bp; xp++)
+ {
+ putc(*xp, mci->mci_out);
+ mci->mci_contentlen++;
+ }
+ if (c == '\n')
+ {
+ fputs(mci->mci_mailer->m_eol,
+ mci->mci_out);
+ mci->mci_contentlen += eol_len;
+ pos = 0;
+ }
+ else
+ {
+ pos += bp - buf;
+ if (c != '\r')
+ *pbp++ = c;
+ }
+ bp = buf;
+
+ /* determine next state */
+ if (c == '\n')
+ ostate = OS_HEAD;
+ else if (c == '\r')
+ ostate = OS_CR;
+ else
+ ostate = OS_INLINE;
+ continue;
+
+ case OS_CR:
+ if (c == '\n')
+ {
+ /* got CRLF */
+ fputs(mci->mci_mailer->m_eol, mci->mci_out);
+ mci->mci_contentlen += eol_len;
+ if (TrafficLogFile != NULL)
+ {
+ fputs(mci->mci_mailer->m_eol,
+ TrafficLogFile);
+ }
+ ostate = OS_HEAD;
+ continue;
+ }
+
+ /* had a naked carriage return */
+ *pbp++ = c;
+ c = '\r';
+ ostate = OS_INLINE;
+ goto putch;
+
+ case OS_INLINE:
+ if (c == '\r')
+ {
+ ostate = OS_CR;
+ continue;
+ }
+#if _FFR_NONULLS
+ if (c == '\0' &&
+ bitnset(M_NONULLS, mci->mci_mailer->m_flags))
+ break;
+#endif
+putch:
+ if (mci->mci_mailer->m_linelimit > 0 &&
+ pos > mci->mci_mailer->m_linelimit &&
+ c != '\n')
+ {
+ putc('!', mci->mci_out);
+ mci->mci_contentlen++;
+ fputs(mci->mci_mailer->m_eol, mci->mci_out);
+ mci->mci_contentlen += eol_len;
+ if (TrafficLogFile != NULL)
+ {
+ fprintf(TrafficLogFile, "!%s",
+ mci->mci_mailer->m_eol);
+ }
+ ostate = OS_HEAD;
+ *pbp++ = c;
+ continue;
+ }
+ if (c == '\n')
+ {
+ if (TrafficLogFile != NULL)
+ fputs(mci->mci_mailer->m_eol,
+ TrafficLogFile);
+ fputs(mci->mci_mailer->m_eol, mci->mci_out);
+ mci->mci_contentlen += eol_len;
+ pos = 0;
+ ostate = OS_HEAD;
+ }
+ else
+ {
+ if (TrafficLogFile != NULL)
+ putc(c, TrafficLogFile);
+ putc(c, mci->mci_out);
+ mci->mci_contentlen++;
+ pos++;
+ ostate = OS_INLINE;
+ }
+ break;
+ }
+ }
+
+ /* make sure we are at the beginning of a line */
+ if (bp > buf)
+ {
+ if (TrafficLogFile != NULL)
+ {
+ for (xp = buf; xp < bp; xp++)
+ putc(*xp, TrafficLogFile);
+ }
+ for (xp = buf; xp < bp; xp++)
+ {
+ putc(*xp, mci->mci_out);
+ mci->mci_contentlen++;
+ }
+ pos += bp - buf;
+ }
+ if (pos > 0)
+ {
+ if (TrafficLogFile != NULL)
+ fputs(mci->mci_mailer->m_eol, TrafficLogFile);
+ fputs(mci->mci_mailer->m_eol, mci->mci_out);
+ mci->mci_contentlen += eol_len;
+ }
+ }
+
+ if (ferror(e->e_dfp))
+ {
+ syserr("putbody: df%s: read error", e->e_id);
+ ExitStat = EX_IOERR;
+ }
+
+endofmessage:
+ /* some mailers want extra blank line at end of message */
+ if (bitnset(M_BLANKEND, mci->mci_mailer->m_flags) &&
+ buf[0] != '\0' && buf[0] != '\n')
+ putline("", mci);
+
+ (void) fflush(mci->mci_out);
+ if (ferror(mci->mci_out) && errno != EPIPE)
+ {
+ syserr("putbody: write error");
+ ExitStat = EX_IOERR;
+ }
+ errno = 0;
+}
+ /*
+** MAILFILE -- Send a message to a file.
+**
+** If the file has the setuid/setgid bits set, but NO execute
+** bits, sendmail will try to become the owner of that file
+** rather than the real user. Obviously, this only works if
+** sendmail runs as root.
+**
+** This could be done as a subordinate mailer, except that it
+** is used implicitly to save messages in ~/dead.letter. We
+** view this as being sufficiently important as to include it
+** here. For example, if the system is dying, we shouldn't have
+** to create another process plus some pipes to save the message.
+**
+** Parameters:
+** filename -- the name of the file to send to.
+** mailer -- mailer definition for recipient -- if NULL,
+** use FileMailer.
+** ctladdr -- the controlling address header -- includes
+** the userid/groupid to be when sending.
+** sfflags -- flags for opening.
+** e -- the current envelope.
+**
+** Returns:
+** The exit code associated with the operation.
+**
+** Side Effects:
+** none.
+*/
+
+static jmp_buf CtxMailfileTimeout;
+static void mailfiletimeout __P((void));
+
+int
+mailfile(filename, mailer, ctladdr, sfflags, e)
+ char *volatile filename;
+ MAILER *volatile mailer;
+ ADDRESS *ctladdr;
+ volatile int sfflags;
+ register ENVELOPE *e;
+{
+ register FILE *f;
+ register pid_t pid = -1;
+ volatile int mode = ST_MODE_NOFILE;
+ bool suidwarn = geteuid() == 0;
+ char *p;
+ EVENT *ev;
+
+ if (tTd(11, 1))
+ {
+ printf("mailfile %s\n ctladdr=", filename);
+ printaddr(ctladdr, FALSE);
+ }
+
+ if (mailer == NULL)
+ mailer = FileMailer;
+
+ if (e->e_xfp != NULL)
+ fflush(e->e_xfp);
+
+ /*
+ ** Special case /dev/null. This allows us to restrict file
+ ** delivery to regular files only.
+ */
+
+ if (strcmp(filename, "/dev/null") == 0)
+ return EX_OK;
+
+ /* check for 8-bit available */
+ if (bitset(EF_HAS8BIT, e->e_flags) &&
+ bitnset(M_7BITS, mailer->m_flags) &&
+ (bitset(EF_DONT_MIME, e->e_flags) ||
+ !(bitset(MM_MIME8BIT, MimeMode) ||
+ (bitset(EF_IS_MIME, e->e_flags) &&
+ bitset(MM_CVTMIME, MimeMode)))))
+ {
+ usrerr("554 Cannot send 8-bit data to 7-bit destination");
+ e->e_status = "5.6.3";
+ return(EX_DATAERR);
+ }
+
+ /*
+ ** Fork so we can change permissions here.
+ ** Note that we MUST use fork, not vfork, because of
+ ** the complications of calling subroutines, etc.
+ */
+
+ DOFORK(fork);
+
+ if (pid < 0)
+ return (EX_OSERR);
+ else if (pid == 0)
+ {
+ /* child -- actually write to file */
+ struct stat stb;
+ MCI mcibuf;
+ volatile int oflags = O_WRONLY|O_APPEND;
+
+ if (e->e_lockfp != NULL)
+ (void) close(fileno(e->e_lockfp));
+
+ (void) setsignal(SIGINT, SIG_DFL);
+ (void) setsignal(SIGHUP, SIG_DFL);
+ (void) setsignal(SIGTERM, SIG_DFL);
+ (void) umask(OldUmask);
+ e->e_to = filename;
+ ExitStat = EX_OK;
+
+ if (setjmp(CtxMailfileTimeout) != 0)
+ {
+ exit(EX_TEMPFAIL);
+ }
+
+ if (TimeOuts.to_fileopen > 0)
+ ev = setevent(TimeOuts.to_fileopen, mailfiletimeout, 0);
+ else
+ ev = NULL;
+
+#ifdef HASLSTAT
+ if (lstat(filename, &stb) < 0)
+#else
+ if (stat(filename, &stb) < 0)
+#endif
+ {
+ stb.st_mode = ST_MODE_NOFILE;
+ mode = FileMode;
+ oflags |= O_CREAT|O_EXCL;
+ }
+ else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, stb.st_mode) ||
+ (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail) &&
+ stb.st_nlink != 1) ||
+ (SafeFileEnv != NULL && !S_ISREG(stb.st_mode)))
+ exit(EX_CANTCREAT);
+ if (mode == ST_MODE_NOFILE)
+ mode = stb.st_mode;
+
+ /* limit the errors to those actually caused in the child */
+ errno = 0;
+ ExitStat = EX_OK;
+
+ if (ctladdr != NULL || bitset(SFF_RUNASREALUID, sfflags))
+ {
+ /* ignore setuid and setgid bits */
+ mode &= ~(S_ISGID|S_ISUID);
+ }
+
+ /* we have to open the dfile BEFORE setuid */
+ if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
+ {
+ char *df = queuename(e, 'd');
+
+ e->e_dfp = fopen(df, "r");
+ if (e->e_dfp == NULL)
+ {
+ syserr("mailfile: Cannot open %s for %s from %s",
+ df, e->e_to, e->e_from.q_paddr);
+ }
+ }
+
+ /* select a new user to run as */
+ if (!bitset(SFF_RUNASREALUID, sfflags))
+ {
+ if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
+ {
+ RealUserName = NULL;
+ RealUid = mailer->m_uid;
+ }
+ else if (bitset(S_ISUID, mode))
+ {
+ RealUserName = NULL;
+ RealUid = stb.st_uid;
+ }
+ else if (ctladdr != NULL && ctladdr->q_uid != 0)
+ {
+ if (ctladdr->q_ruser != NULL)
+ RealUserName = ctladdr->q_ruser;
+ else
+ RealUserName = ctladdr->q_user;
+ RealUid = ctladdr->q_uid;
+ }
+ else if (mailer != NULL && mailer->m_uid != 0)
+ {
+ RealUserName = DefUser;
+ RealUid = mailer->m_uid;
+ }
+ else
+ {
+ RealUserName = DefUser;
+ RealUid = DefUid;
+ }
+
+ /* select a new group to run as */
+ if (bitnset(M_SPECIFIC_UID, mailer->m_flags))
+ RealGid = mailer->m_gid;
+ else if (bitset(S_ISGID, mode))
+ RealGid = stb.st_gid;
+ else if (ctladdr != NULL && ctladdr->q_uid != 0)
+ RealGid = ctladdr->q_gid;
+ else if (mailer != NULL && mailer->m_gid != 0)
+ RealGid = mailer->m_gid;
+ else
+ RealGid = DefGid;
+ }
+
+ /* last ditch */
+ if (!bitset(SFF_ROOTOK, sfflags))
+ {
+ if (RealUid == 0)
+ RealUid = DefUid;
+ if (RealGid == 0)
+ RealGid = DefGid;
+ }
+
+ /* set group id list (needs /etc/group access) */
+ if (RealUserName != NULL && !DontInitGroups)
+ {
+ if (initgroups(RealUserName, RealGid) == -1 && suidwarn)
+ syserr("mailfile: initgroups(%s, %d) failed",
+ RealUserName, RealGid);
+ }
+ else
+ {
+ GIDSET_T gidset[1];
+
+ gidset[0] = RealGid;
+ if (setgroups(1, gidset) == -1 && suidwarn)
+ syserr("mailfile: setgroups() failed");
+ }
+
+ /* if you have a safe environment, go into it */
+ if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
+ {
+ int i;
+
+ if (chroot(SafeFileEnv) < 0)
+ {
+ syserr("mailfile: Cannot chroot(%s)",
+ SafeFileEnv);
+ exit(EX_CANTCREAT);
+ }
+ i = strlen(SafeFileEnv);
+ if (strncmp(SafeFileEnv, filename, i) == 0)
+ filename += i;
+ }
+ if (chdir("/") < 0)
+ syserr("mailfile: cannot chdir(/)");
+
+ /* now reset the group and user ids */
+ endpwent();
+ if (setgid(RealGid) < 0 && suidwarn)
+ syserr("mailfile: setgid(%ld) failed", (long) RealGid);
+ vendor_set_uid(RealUid);
+ if (setuid(RealUid) < 0 && suidwarn)
+ syserr("mailfile: setuid(%ld) failed", (long) RealUid);
+
+ /* move into some "safe" directory */
+ if (mailer->m_execdir != NULL)
+ {
+ char *q;
+ char buf[MAXLINE + 1];
+
+ for (p = mailer->m_execdir; p != NULL; p = q)
+ {
+ q = strchr(p, ':');
+ if (q != NULL)
+ *q = '\0';
+ expand(p, buf, sizeof buf, e);
+ if (q != NULL)
+ *q++ = ':';
+ if (tTd(11, 20))
+ printf("mailfile: trydir %s\n",
+ buf);
+ if (buf[0] != '\0' && chdir(buf) >= 0)
+ break;
+ }
+ }
+
+ sfflags |= SFF_NOPATHCHECK;
+ if (!bitset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
+ sfflags |= SFF_NOSLINK;
+ if (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
+ sfflags |= SFF_NOHLINK;
+ sfflags &= ~SFF_OPENASROOT;
+ f = safefopen(filename, oflags, FileMode, sfflags);
+ if (f == NULL)
+ {
+ message("554 cannot open %s: %s",
+ shortenstring(filename, MAXSHORTSTR),
+ errstring(errno));
+ exit(EX_CANTCREAT);
+ }
+ if (filechanged(filename, fileno(f), &stb))
+ {
+ message("554 file changed after open");
+ exit(EX_CANTCREAT);
+ }
+ if (fstat(fileno(f), &stb) < 0)
+ {
+ message("554 cannot fstat %s", errstring(errno));
+ exit(EX_CANTCREAT);
+ }
+
+ if (ev != NULL)
+ clrevent(ev);
+
+ bzero(&mcibuf, sizeof mcibuf);
+ mcibuf.mci_mailer = mailer;
+ mcibuf.mci_out = f;
+ mcibuf.mci_contentlen = 0;
+ if (bitnset(M_7BITS, mailer->m_flags))
+ mcibuf.mci_flags |= MCIF_7BIT;
+
+ /* clear out per-message flags from connection structure */
+ mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);
+
+ if (bitset(EF_HAS8BIT, e->e_flags) &&
+ !bitset(EF_DONT_MIME, e->e_flags) &&
+ bitnset(M_7BITS, mailer->m_flags))
+ mcibuf.mci_flags |= MCIF_CVT8TO7;
+
+#if MIME7TO8
+ if (bitnset(M_MAKE8BIT, mailer->m_flags) &&
+ !bitset(MCIF_7BIT, mcibuf.mci_flags) &&
+ (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&
+ (strcasecmp(p, "quoted-printable") == 0 ||
+ strcasecmp(p, "base64") == 0) &&
+ (p = hvalue("Content-Type", e->e_header)) != NULL)
+ {
+ /* may want to convert 7 -> 8 */
+ /* XXX should really parse it here -- and use a class XXX */
+ if (strncasecmp(p, "text/plain", 10) == 0 &&
+ (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))
+ mcibuf.mci_flags |= MCIF_CVT7TO8;
+ }
+#endif
+
+ putfromline(&mcibuf, e);
+ (*e->e_puthdr)(&mcibuf, e->e_header, e);
+ (*e->e_putbody)(&mcibuf, e, NULL);
+ putline("\n", &mcibuf);
+ if (fflush(f) < 0 || ferror(f))
+ {
+ message("451 I/O error: %s", errstring(errno));
+ setstat(EX_IOERR);
+ }
+
+ /* reset ISUID & ISGID bits for paranoid systems */
+#if HASFCHMOD
+ (void) fchmod(fileno(f), (MODE_T) stb.st_mode);
+#else
+ (void) chmod(filename, (MODE_T) stb.st_mode);
+#endif
+ (void) xfclose(f, "mailfile", filename);
+ (void) fflush(stdout);
+ setuid(RealUid);
+ exit(ExitStat);
+ /*NOTREACHED*/
+ }
+ else
+ {
+ /* parent -- wait for exit status */
+ int st;
+
+ st = waitfor(pid);
+ if (st == -1)
+ {
+ syserr("mailfile: %s: wait", mailer->m_name);
+ return (EX_SOFTWARE);
+ }
+ if (WIFEXITED(st))
+ return (WEXITSTATUS(st));
+ else
+ {
+ syserr("mailfile: %s: child died on signal %d",
+ mailer->m_name, st);
+ return (EX_UNAVAILABLE);
+ }
+ /*NOTREACHED*/
+ }
+ return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */
+}
+
+static void
+mailfiletimeout()
+{
+ longjmp(CtxMailfileTimeout, 1);
+}
+ /*
+** HOSTSIGNATURE -- return the "signature" for a host.
+**
+** The signature describes how we are going to send this -- it
+** can be just the hostname (for non-Internet hosts) or can be
+** an ordered list of MX hosts.
+**
+** Parameters:
+** m -- the mailer describing this host.
+** host -- the host name.
+** e -- the current envelope.
+**
+** Returns:
+** The signature for this host.
+**
+** Side Effects:
+** Can tweak the symbol table.
+*/
+
+char *
+hostsignature(m, host, e)
+ register MAILER *m;
+ char *host;
+ ENVELOPE *e;
+{
+ register char *p;
+ register STAB *s;
+ int i;
+ int len;
+#if NAMED_BIND
+ int nmx;
+ char *hp;
+ char *endp;
+ int oldoptions = _res.options;
+ char *mxhosts[MAXMXHOSTS + 1];
+#endif
+
+ /*
+ ** Check to see if this uses IPC -- if not, it can't have MX records.
+ */
+
+ p = m->m_mailer;
+ if (strcmp(p, "[IPC]") != 0 && strcmp(p, "[TCP]") != 0)
+ {
+ /* just an ordinary mailer */
+ return host;
+ }
+
+ /*
+ ** Look it up in the symbol table.
+ */
+
+ s = stab(host, ST_HOSTSIG, ST_ENTER);
+ if (s->s_hostsig != NULL)
+ return s->s_hostsig;
+
+ /*
+ ** Not already there -- create a signature.
+ */
+
+#if NAMED_BIND
+ if (ConfigLevel < 2)
+ _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */
+
+ for (hp = host; hp != NULL; hp = endp)
+ {
+ endp = strchr(hp, ':');
+ if (endp != NULL)
+ *endp = '\0';
+
+ if (bitnset(M_NOMX, m->m_flags))
+ {
+ /* skip MX lookups */
+ nmx = 1;
+ mxhosts[0] = hp;
+ }
+ else
+ {
+ auto int rcode;
+
+ nmx = getmxrr(hp, mxhosts, TRUE, &rcode);
+ if (nmx <= 0)
+ {
+ register MCI *mci;
+
+ /* update the connection info for this host */
+ mci = mci_get(hp, m);
+ mci->mci_errno = errno;
+ mci->mci_herrno = h_errno;
+ mci->mci_lastuse = curtime();
+ mci_setstat(mci, rcode, NULL, NULL);
+
+ /* use the original host name as signature */
+ nmx = 1;
+ mxhosts[0] = hp;
+ }
+ }
+
+ len = 0;
+ for (i = 0; i < nmx; i++)
+ {
+ len += strlen(mxhosts[i]) + 1;
+ }
+ if (s->s_hostsig != NULL)
+ len += strlen(s->s_hostsig) + 1;
+ p = xalloc(len);
+ if (s->s_hostsig != NULL)
+ {
+ (void) strcpy(p, s->s_hostsig);
+ free(s->s_hostsig);
+ s->s_hostsig = p;
+ p += strlen(p);
+ *p++ = ':';
+ }
+ else
+ s->s_hostsig = p;
+ for (i = 0; i < nmx; i++)
+ {
+ if (i != 0)
+ *p++ = ':';
+ strcpy(p, mxhosts[i]);
+ p += strlen(p);
+ }
+ if (endp != NULL)
+ *endp++ = ':';
+ }
+ makelower(s->s_hostsig);
+ if (ConfigLevel < 2)
+ _res.options = oldoptions;
+#else
+ /* not using BIND -- the signature is just the host name */
+ s->s_hostsig = host;
+#endif
+ if (tTd(17, 1))
+ printf("hostsignature(%s) = %s\n", host, s->s_hostsig);
+ return s->s_hostsig;
+}
diff --git a/contrib/sendmail/src/domain.c b/contrib/sendmail/src/domain.c
new file mode 100644
index 0000000..e3a5500
--- /dev/null
+++ b/contrib/sendmail/src/domain.c
@@ -0,0 +1,913 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1986, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#include "sendmail.h"
+
+#ifndef lint
+#if NAMED_BIND
+static char sccsid[] = "@(#)domain.c 8.77 (Berkeley) 6/4/98 (with name server)";
+#else
+static char sccsid[] = "@(#)domain.c 8.77 (Berkeley) 6/4/98 (without name server)";
+#endif
+#endif /* not lint */
+
+#if NAMED_BIND
+
+#include <errno.h>
+#include <resolv.h>
+#include <arpa/inet.h>
+
+/*
+** The standard udp packet size PACKETSZ (512) is not sufficient for some
+** nameserver answers containing very many resource records. The resolver
+** may switch to tcp and retry if it detects udp packet overflow.
+** Also note that the resolver routines res_query and res_search return
+** the size of the *un*truncated answer in case the supplied answer buffer
+** it not big enough to accommodate the entire answer.
+*/
+
+#ifndef MAXPACKET
+# define MAXPACKET 8192 /* max packet size used internally by BIND */
+#endif
+
+typedef union
+{
+ HEADER qb1;
+ u_char qb2[MAXPACKET];
+} querybuf;
+
+#ifndef MXHOSTBUFSIZE
+# define MXHOSTBUFSIZE (128 * MAXMXHOSTS)
+#endif
+
+static char MXHostBuf[MXHOSTBUFSIZE];
+
+#ifndef MAXDNSRCH
+# define MAXDNSRCH 6 /* number of possible domains to search */
+#endif
+
+#ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef NO_DATA
+# define NO_DATA NO_ADDRESS
+#endif
+
+#ifndef HFIXEDSZ
+# define HFIXEDSZ 12 /* sizeof(HEADER) */
+#endif
+
+#define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */
+
+#if defined(__RES) && (__RES >= 19940415)
+# define RES_UNC_T char *
+#else
+# define RES_UNC_T u_char *
+#endif
+ /*
+** GETMXRR -- get MX resource records for a domain
+**
+** Parameters:
+** host -- the name of the host to MX.
+** mxhosts -- a pointer to a return buffer of MX records.
+** droplocalhost -- If TRUE, all MX records less preferred
+** than the local host (as determined by $=w) will
+** be discarded.
+** rcode -- a pointer to an EX_ status code.
+**
+** Returns:
+** The number of MX records found.
+** -1 if there is an internal failure.
+** If no MX records are found, mxhosts[0] is set to host
+** and 1 is returned.
+*/
+
+int
+getmxrr(host, mxhosts, droplocalhost, rcode)
+ char *host;
+ char **mxhosts;
+ bool droplocalhost;
+ int *rcode;
+{
+ register u_char *eom, *cp;
+ register int i, j, n;
+ int nmx = 0;
+ register char *bp;
+ HEADER *hp;
+ querybuf answer;
+ int ancount, qdcount, buflen;
+ bool seenlocal = FALSE;
+ u_short pref, type;
+ u_short localpref = 256;
+ char *fallbackMX = FallBackMX;
+ bool trycanon = FALSE;
+ int (*resfunc)();
+ extern int res_query(), res_search();
+ u_short prefer[MAXMXHOSTS];
+ int weight[MAXMXHOSTS];
+ extern int mxrand __P((char *));
+
+ if (tTd(8, 2))
+ printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost);
+
+ if (fallbackMX != NULL && droplocalhost &&
+ wordinclass(fallbackMX, 'w'))
+ {
+ /* don't use fallback for this pass */
+ fallbackMX = NULL;
+ }
+
+ *rcode = EX_OK;
+
+ /* efficiency hack -- numeric or non-MX lookups */
+ if (host[0] == '[')
+ goto punt;
+
+ /*
+ ** If we don't have MX records in our host switch, don't
+ ** try for MX records. Note that this really isn't "right",
+ ** since we might be set up to try NIS first and then DNS;
+ ** if the host is found in NIS we really shouldn't be doing
+ ** MX lookups. However, that should be a degenerate case.
+ */
+
+ if (!UseNameServer)
+ goto punt;
+ if (HasWildcardMX && ConfigLevel >= 6)
+ resfunc = res_query;
+ else
+ resfunc = res_search;
+
+ errno = 0;
+ n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer));
+ if (n < 0)
+ {
+ if (tTd(8, 1))
+ printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n",
+ (host == NULL) ? "<NULL>" : host, errno, h_errno);
+ switch (h_errno)
+ {
+ case NO_DATA:
+ trycanon = TRUE;
+ /* fall through */
+
+ case NO_RECOVERY:
+ /* no MX data on this host */
+ goto punt;
+
+ case HOST_NOT_FOUND:
+#if BROKEN_RES_SEARCH
+ case 0: /* Ultrix resolver retns failure w/ h_errno=0 */
+#endif
+ /* host doesn't exist in DNS; might be in /etc/hosts */
+ trycanon = TRUE;
+ *rcode = EX_NOHOST;
+ goto punt;
+
+ case TRY_AGAIN:
+ case -1:
+ /* couldn't connect to the name server */
+ if (fallbackMX != NULL)
+ {
+ /* name server is hosed -- push to fallback */
+ mxhosts[nmx++] = fallbackMX;
+ return nmx;
+ }
+ /* it might come up later; better queue it up */
+ *rcode = EX_TEMPFAIL;
+ break;
+
+ default:
+ syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n",
+ host, h_errno);
+ *rcode = EX_OSERR;
+ break;
+ }
+
+ /* irreconcilable differences */
+ return (-1);
+ }
+
+ /* avoid problems after truncation in tcp packets */
+ if (n > sizeof(answer))
+ n = sizeof(answer);
+
+ /* find first satisfactory answer */
+ hp = (HEADER *)&answer;
+ cp = (u_char *)&answer + HFIXEDSZ;
+ eom = (u_char *)&answer + n;
+ for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
+ if ((n = dn_skipname(cp, eom)) < 0)
+ goto punt;
+ buflen = sizeof(MXHostBuf) - 1;
+ bp = MXHostBuf;
+ ancount = ntohs(hp->ancount);
+ while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
+ {
+ if ((n = dn_expand((u_char *)&answer,
+ eom, cp, (RES_UNC_T) bp, buflen)) < 0)
+ break;
+ cp += n;
+ GETSHORT(type, cp);
+ cp += INT16SZ + INT32SZ;
+ GETSHORT(n, cp);
+ if (type != T_MX)
+ {
+ if (tTd(8, 8) || _res.options & RES_DEBUG)
+ printf("unexpected answer type %d, size %d\n",
+ type, n);
+ cp += n;
+ continue;
+ }
+ GETSHORT(pref, cp);
+ if ((n = dn_expand((u_char *)&answer, eom, cp,
+ (RES_UNC_T) bp, buflen)) < 0)
+ break;
+ cp += n;
+ if (wordinclass(bp, 'w'))
+ {
+ if (tTd(8, 3))
+ printf("found localhost (%s) in MX list, pref=%d\n",
+ bp, pref);
+ if (droplocalhost)
+ {
+ if (!seenlocal || pref < localpref)
+ localpref = pref;
+ seenlocal = TRUE;
+ continue;
+ }
+ weight[nmx] = 0;
+ }
+ else
+ weight[nmx] = mxrand(bp);
+ prefer[nmx] = pref;
+ mxhosts[nmx++] = bp;
+ n = strlen(bp);
+ bp += n;
+ if (bp[-1] != '.')
+ {
+ *bp++ = '.';
+ n++;
+ }
+ *bp++ = '\0';
+ buflen -= n + 1;
+ }
+
+ /* sort the records */
+ for (i = 0; i < nmx; i++)
+ {
+ for (j = i + 1; j < nmx; j++)
+ {
+ if (prefer[i] > prefer[j] ||
+ (prefer[i] == prefer[j] && weight[i] > weight[j]))
+ {
+ register int temp;
+ register char *temp1;
+
+ temp = prefer[i];
+ prefer[i] = prefer[j];
+ prefer[j] = temp;
+ temp1 = mxhosts[i];
+ mxhosts[i] = mxhosts[j];
+ mxhosts[j] = temp1;
+ temp = weight[i];
+ weight[i] = weight[j];
+ weight[j] = temp;
+ }
+ }
+ if (seenlocal && prefer[i] >= localpref)
+ {
+ /* truncate higher preference part of list */
+ nmx = i;
+ }
+ }
+
+ /* delete duplicates from list (yes, some bozos have duplicates) */
+ for (i = 0; i < nmx - 1; )
+ {
+ if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0)
+ i++;
+ else
+ {
+ /* compress out duplicate */
+ for (j = i + 1; j < nmx; j++)
+ mxhosts[j] = mxhosts[j + 1];
+ nmx--;
+ }
+ }
+
+ if (nmx == 0)
+ {
+punt:
+ if (seenlocal &&
+ (!TryNullMXList || sm_gethostbyname(host) == NULL))
+ {
+ /*
+ ** If we have deleted all MX entries, this is
+ ** an error -- we should NEVER send to a host that
+ ** has an MX, and this should have been caught
+ ** earlier in the config file.
+ **
+ ** Some sites prefer to go ahead and try the
+ ** A record anyway; that case is handled by
+ ** setting TryNullMXList. I believe this is a
+ ** bad idea, but it's up to you....
+ */
+
+ *rcode = EX_CONFIG;
+ syserr("MX list for %s points back to %s",
+ host, MyHostName);
+ return -1;
+ }
+ if (strlen(host) >= (SIZE_T) sizeof MXHostBuf)
+ {
+ *rcode = EX_CONFIG;
+ syserr("Host name %s too long",
+ shortenstring(host, MAXSHORTSTR));
+ return -1;
+ }
+ snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host);
+ mxhosts[0] = MXHostBuf;
+ if (host[0] == '[')
+ {
+ register char *p;
+
+ /* this may be an MX suppression-style address */
+ p = strchr(MXHostBuf, ']');
+ if (p != NULL)
+ {
+ *p = '\0';
+ if (inet_addr(&MXHostBuf[1]) != INADDR_NONE)
+ {
+ nmx++;
+ *p = ']';
+ }
+ else
+ {
+ trycanon = TRUE;
+ mxhosts[0]++;
+ }
+ }
+ }
+ if (trycanon &&
+ getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE))
+ {
+ bp = &MXHostBuf[strlen(MXHostBuf)];
+ if (bp[-1] != '.')
+ {
+ *bp++ = '.';
+ *bp = '\0';
+ }
+ nmx = 1;
+ }
+ }
+
+ /* if we have a default lowest preference, include that */
+ if (fallbackMX != NULL && !seenlocal)
+ mxhosts[nmx++] = fallbackMX;
+
+ return (nmx);
+}
+ /*
+** MXRAND -- create a randomizer for equal MX preferences
+**
+** If two MX hosts have equal preferences we want to randomize
+** the selection. But in order for signatures to be the same,
+** we need to randomize the same way each time. This function
+** computes a pseudo-random hash function from the host name.
+**
+** Parameters:
+** host -- the name of the host.
+**
+** Returns:
+** A random but repeatable value based on the host name.
+**
+** Side Effects:
+** none.
+*/
+
+int
+mxrand(host)
+ register char *host;
+{
+ int hfunc;
+ static unsigned int seed;
+
+ if (seed == 0)
+ {
+ seed = (int) curtime() & 0xffff;
+ if (seed == 0)
+ seed++;
+ }
+
+ if (tTd(17, 9))
+ printf("mxrand(%s)", host);
+
+ hfunc = seed;
+ while (*host != '\0')
+ {
+ int c = *host++;
+
+ if (isascii(c) && isupper(c))
+ c = tolower(c);
+ hfunc = ((hfunc << 1) ^ c) % 2003;
+ }
+
+ hfunc &= 0xff;
+ hfunc++;
+
+ if (tTd(17, 9))
+ printf(" = %d\n", hfunc);
+ return hfunc;
+}
+ /*
+** BESTMX -- find the best MX for a name
+**
+** This is really a hack, but I don't see any obvious way
+** to generalize it at the moment.
+*/
+
+/* ARGSUSED3 */
+char *
+bestmx_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ int nmx;
+ auto int rcode;
+ int saveopts = _res.options;
+ int i, len = 0;
+ char *p;
+ char *mxhosts[MAXMXHOSTS + 1];
+ char buf[MXHOSTBUFSIZE + 1];
+
+ _res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
+ nmx = getmxrr(name, mxhosts, FALSE, &rcode);
+ _res.options = saveopts;
+ if (nmx <= 0)
+ return NULL;
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ return map_rewrite(map, name, strlen(name), NULL);
+ if ((map->map_coldelim == '\0') || (nmx == 1))
+ return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
+
+ /*
+ ** We were given a -z flag (return all MXs) and there are multiple
+ ** ones. We need to build them all into a list.
+ */
+ p = buf;
+ for (i = 0; i < nmx; i++)
+ {
+ int slen;
+
+ if (strchr(mxhosts[i], map->map_coldelim) != NULL)
+ {
+ syserr("bestmx_map_lookup: MX host %.64s includes map delimiter character 0x%02X",
+ mxhosts[i], map->map_coldelim);
+ return NULL;
+ }
+ slen = strlen(mxhosts[i]);
+ if (len + slen + 2 > sizeof buf)
+ break;
+ if (i > 0)
+ {
+ *p++ = map->map_coldelim;
+ len++;
+ }
+ strcpy(p, mxhosts[i]);
+ p += slen;
+ len += slen;
+ }
+ return map_rewrite(map, buf, len, av);
+}
+ /*
+** DNS_GETCANONNAME -- get the canonical name for named host using DNS
+**
+** This algorithm tries to be smart about wildcard MX records.
+** This is hard to do because DNS doesn't tell is if we matched
+** against a wildcard or a specific MX.
+**
+** We always prefer A & CNAME records, since these are presumed
+** to be specific.
+**
+** If we match an MX in one pass and lose it in the next, we use
+** the old one. For example, consider an MX matching *.FOO.BAR.COM.
+** A hostname bletch.foo.bar.com will match against this MX, but
+** will stop matching when we try bletch.bar.com -- so we know
+** that bletch.foo.bar.com must have been right. This fails if
+** there was also an MX record matching *.BAR.COM, but there are
+** some things that just can't be fixed.
+**
+** Parameters:
+** host -- a buffer containing the name of the host.
+** This is a value-result parameter.
+** hbsize -- the size of the host buffer.
+** trymx -- if set, try MX records as well as A and CNAME.
+** statp -- pointer to place to store status.
+**
+** Returns:
+** TRUE -- if the host matched.
+** FALSE -- otherwise.
+*/
+
+bool
+dns_getcanonname(host, hbsize, trymx, statp)
+ char *host;
+ int hbsize;
+ bool trymx;
+ int *statp;
+{
+ register u_char *eom, *ap;
+ register char *cp;
+ register int n;
+ HEADER *hp;
+ querybuf answer;
+ int ancount, qdcount;
+ int ret;
+ char **domain;
+ int type;
+ char **dp;
+ char *mxmatch;
+ bool amatch;
+ bool gotmx = FALSE;
+ int qtype;
+ int loopcnt;
+ char *xp;
+ char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)];
+ char *searchlist[MAXDNSRCH+2];
+ extern char *gethostalias __P((char *));
+
+ if (tTd(8, 2))
+ printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
+
+ if ((_res.options & RES_INIT) == 0 && res_init() == -1)
+ {
+ *statp = EX_UNAVAILABLE;
+ return FALSE;
+ }
+
+ /*
+ ** Initialize domain search list. If there is at least one
+ ** dot in the name, search the unmodified name first so we
+ ** find "vse.CS" in Czechoslovakia instead of in the local
+ ** domain (e.g., vse.CS.Berkeley.EDU).
+ **
+ ** Older versions of the resolver could create this
+ ** list by tearing apart the host name.
+ */
+
+ loopcnt = 0;
+cnameloop:
+ /* Check for dots in the name */
+ for (cp = host, n = 0; *cp != '\0'; cp++)
+ if (*cp == '.')
+ n++;
+
+ /*
+ ** If this is a simple name, determine whether it matches an
+ ** alias in the file defined by the environment variable HOSTALIASES.
+ */
+ if (n == 0 && (xp = gethostalias(host)) != NULL)
+ {
+ if (loopcnt++ > MAXCNAMEDEPTH)
+ {
+ syserr("loop in ${HOSTALIASES} file");
+ }
+ else
+ {
+ strncpy(host, xp, hbsize);
+ host[hbsize - 1] = '\0';
+ goto cnameloop;
+ }
+ }
+
+ /*
+ ** Build the search list.
+ ** If there is at least one dot in name, start with a null
+ ** domain to search the unmodified name first.
+ ** If name does not end with a dot and search up local domain
+ ** tree desired, append each local domain component to the
+ ** search list; if name contains no dots and default domain
+ ** name is desired, append default domain name to search list;
+ ** else if name ends in a dot, remove that dot.
+ */
+
+ dp = searchlist;
+ if (n > 0)
+ *dp++ = "";
+ if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options))
+ {
+ for (domain = _res.dnsrch; *domain != NULL; )
+ *dp++ = *domain++;
+ }
+ else if (n == 0 && bitset(RES_DEFNAMES, _res.options))
+ {
+ *dp++ = _res.defdname;
+ }
+ else if (*cp == '.')
+ {
+ *cp = '\0';
+ }
+ *dp = NULL;
+
+ /*
+ ** Now loop through the search list, appending each domain in turn
+ ** name and searching for a match.
+ */
+
+ mxmatch = NULL;
+ qtype = T_ANY;
+
+ for (dp = searchlist; *dp != NULL; )
+ {
+ if (qtype == T_ANY)
+ gotmx = FALSE;
+ if (tTd(8, 5))
+ printf("dns_getcanonname: trying %s.%s (%s)\n",
+ host, *dp,
+ qtype == T_ANY ? "ANY" : qtype == T_A ? "A" :
+ qtype == T_MX ? "MX" : "???");
+ ret = res_querydomain(host, *dp, C_IN, qtype,
+ answer.qb2, sizeof(answer.qb2));
+ if (ret <= 0)
+ {
+ if (tTd(8, 7))
+ printf("\tNO: errno=%d, h_errno=%d\n",
+ errno, h_errno);
+
+ if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
+ {
+ /* the name server seems to be down */
+ h_errno = TRY_AGAIN;
+ *statp = EX_TEMPFAIL;
+ return FALSE;
+ }
+
+ if (h_errno != HOST_NOT_FOUND)
+ {
+ /* might have another type of interest */
+ if (qtype == T_ANY)
+ {
+ qtype = T_A;
+ continue;
+ }
+ else if (qtype == T_A && !gotmx && trymx)
+ {
+ qtype = T_MX;
+ continue;
+ }
+ }
+
+ /* definite no -- try the next domain */
+ dp++;
+ qtype = T_ANY;
+ continue;
+ }
+ else if (tTd(8, 7))
+ printf("\tYES\n");
+
+ /* avoid problems after truncation in tcp packets */
+ if (ret > sizeof(answer))
+ ret = sizeof(answer);
+
+ /*
+ ** Appear to have a match. Confirm it by searching for A or
+ ** CNAME records. If we don't have a local domain
+ ** wild card MX record, we will accept MX as well.
+ */
+
+ hp = (HEADER *) &answer;
+ ap = (u_char *) &answer + HFIXEDSZ;
+ eom = (u_char *) &answer + ret;
+
+ /* skip question part of response -- we know what we asked */
+ for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
+ {
+ if ((ret = dn_skipname(ap, eom)) < 0)
+ {
+ if (tTd(8, 20))
+ printf("qdcount failure (%d)\n",
+ ntohs(hp->qdcount));
+ *statp = EX_SOFTWARE;
+ return FALSE; /* ???XXX??? */
+ }
+ }
+
+ amatch = FALSE;
+ for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom;
+ ap += n)
+ {
+ n = dn_expand((u_char *) &answer, eom, ap,
+ (RES_UNC_T) nbuf, sizeof nbuf);
+ if (n < 0)
+ break;
+ ap += n;
+ GETSHORT(type, ap);
+ ap += INT16SZ + INT32SZ;
+ GETSHORT(n, ap);
+ switch (type)
+ {
+ case T_MX:
+ gotmx = TRUE;
+ if (**dp != '\0' && HasWildcardMX)
+ {
+ /*
+ ** If we are using MX matches and have
+ ** not yet gotten one, save this one
+ ** but keep searching for an A or
+ ** CNAME match.
+ */
+
+ if (trymx && mxmatch == NULL)
+ mxmatch = *dp;
+ continue;
+ }
+
+ /*
+ ** If we did not append a domain name, this
+ ** must have been a canonical name to start
+ ** with. Even if we did append a domain name,
+ ** in the absence of a wildcard MX this must
+ ** still be a real MX match.
+ ** Such MX matches are as good as an A match,
+ ** fall through.
+ */
+
+ case T_A:
+ /* Flag that a good match was found */
+ amatch = TRUE;
+
+ /* continue in case a CNAME also exists */
+ continue;
+
+ case T_CNAME:
+ if (DontExpandCnames)
+ {
+ /* got CNAME -- guaranteed canonical */
+ amatch = TRUE;
+ break;
+ }
+
+ if (loopcnt++ > MAXCNAMEDEPTH)
+ {
+ /*XXX should notify postmaster XXX*/
+ message("DNS failure: CNAME loop for %s",
+ host);
+ if (CurEnv->e_message == NULL)
+ {
+ char ebuf[MAXLINE];
+
+ snprintf(ebuf, sizeof ebuf,
+ "Deferred: DNS failure: CNAME loop for %.100s",
+ host);
+ CurEnv->e_message = newstr(ebuf);
+ }
+ h_errno = NO_RECOVERY;
+ *statp = EX_CONFIG;
+ return FALSE;
+ }
+
+ /* value points at name */
+ if ((ret = dn_expand((u_char *)&answer,
+ eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0)
+ break;
+ (void)strncpy(host, nbuf, hbsize); /* XXX */
+ host[hbsize - 1] = '\0';
+
+ /*
+ ** RFC 1034 section 3.6 specifies that CNAME
+ ** should point at the canonical name -- but
+ ** urges software to try again anyway.
+ */
+
+ goto cnameloop;
+
+ default:
+ /* not a record of interest */
+ continue;
+ }
+ }
+
+ if (amatch)
+ {
+ /*
+ ** Got a good match -- either an A, CNAME, or an
+ ** exact MX record. Save it and get out of here.
+ */
+
+ mxmatch = *dp;
+ break;
+ }
+
+ /*
+ ** Nothing definitive yet.
+ ** If this was a T_ANY query, we don't really know what
+ ** was returned -- it might have been a T_NS,
+ ** for example. Try T_A to be more specific
+ ** during the next pass.
+ ** If this was a T_A query and we haven't yet found a MX
+ ** match, try T_MX if allowed to do so.
+ ** Otherwise, try the next domain.
+ */
+
+ if (qtype == T_ANY)
+ qtype = T_A;
+ else if (qtype == T_A && !gotmx && trymx)
+ qtype = T_MX;
+ else
+ {
+ qtype = T_ANY;
+ dp++;
+ }
+ }
+
+ /* if nothing was found, we are done */
+ if (mxmatch == NULL)
+ {
+ *statp = EX_NOHOST;
+ return FALSE;
+ }
+
+ /*
+ ** Create canonical name and return.
+ ** If saved domain name is null, name was already canonical.
+ ** Otherwise append the saved domain name.
+ */
+
+ (void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host,
+ *mxmatch == '\0' ? "" : ".",
+ MAXDNAME, mxmatch);
+ strncpy(host, nbuf, hbsize);
+ host[hbsize - 1] = '\0';
+ if (tTd(8, 5))
+ printf("dns_getcanonname: %s\n", host);
+ *statp = EX_OK;
+ return TRUE;
+}
+
+
+
+char *
+gethostalias(host)
+ char *host;
+{
+ char *fname;
+ FILE *fp;
+ register char *p = NULL;
+ int sff = SFF_REGONLY;
+ char buf[MAXLINE];
+ static char hbuf[MAXDNAME];
+
+ if (DontLockReadFiles)
+ sff |= SFF_NOLOCK;
+ fname = getenv("HOSTALIASES");
+ if (fname == NULL ||
+ (fp = safefopen(fname, O_RDONLY, 0, sff)) == NULL)
+ return NULL;
+ while (fgets(buf, sizeof buf, fp) != NULL)
+ {
+ for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++)
+ continue;
+ if (*p == 0)
+ {
+ /* syntax error */
+ continue;
+ }
+ *p++ = '\0';
+ if (strcasecmp(buf, host) == 0)
+ break;
+ }
+
+ if (feof(fp))
+ {
+ /* no match */
+ fclose(fp);
+ return NULL;
+ }
+ fclose(fp);
+
+ /* got a match; extract the equivalent name */
+ while (*p != '\0' && isascii(*p) && isspace(*p))
+ p++;
+ host = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ *p = '\0';
+ strncpy(hbuf, host, sizeof hbuf - 1);
+ hbuf[sizeof hbuf - 1] = '\0';
+ return hbuf;
+}
+
+#endif /* NAMED_BIND */
diff --git a/contrib/sendmail/src/envelope.c b/contrib/sendmail/src/envelope.c
new file mode 100644
index 0000000..092148a
--- /dev/null
+++ b/contrib/sendmail/src/envelope.c
@@ -0,0 +1,938 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)envelope.c 8.117 (Berkeley) 6/4/98";
+#endif /* not lint */
+
+#include "sendmail.h"
+
+/*
+** NEWENVELOPE -- allocate a new envelope
+**
+** Supports inheritance.
+**
+** Parameters:
+** e -- the new envelope to fill in.
+** parent -- the envelope to be the parent of e.
+**
+** Returns:
+** e.
+**
+** Side Effects:
+** none.
+*/
+
+ENVELOPE *
+newenvelope(e, parent)
+ register ENVELOPE *e;
+ register ENVELOPE *parent;
+{
+ if (e == parent && e->e_parent != NULL)
+ parent = e->e_parent;
+ clearenvelope(e, TRUE);
+ if (e == CurEnv)
+ bcopy((char *) &NullAddress, (char *) &e->e_from, sizeof e->e_from);
+ else
+ bcopy((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from);
+ e->e_parent = parent;
+ e->e_ctime = curtime();
+ if (parent != NULL)
+ e->e_msgpriority = parent->e_msgsize;
+ e->e_puthdr = putheader;
+ e->e_putbody = putbody;
+ if (CurEnv->e_xfp != NULL)
+ (void) fflush(CurEnv->e_xfp);
+
+ return (e);
+}
+ /*
+** DROPENVELOPE -- deallocate an envelope.
+**
+** Parameters:
+** e -- the envelope to deallocate.
+** fulldrop -- if set, do return receipts.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** housekeeping necessary to dispose of an envelope.
+** Unlocks this queue file.
+*/
+
+void
+dropenvelope(e, fulldrop)
+ register ENVELOPE *e;
+ bool fulldrop;
+{
+ bool queueit = FALSE;
+ bool message_timeout = FALSE;
+ bool failure_return = FALSE;
+ bool delay_return = FALSE;
+ bool success_return = FALSE;
+ register ADDRESS *q;
+ char *id = e->e_id;
+ char buf[MAXLINE];
+
+ if (tTd(50, 1))
+ {
+ extern void printenvflags __P((ENVELOPE *));
+
+ printf("dropenvelope %lx: id=", (u_long) e);
+ xputs(e->e_id);
+ printf(", flags=");
+ printenvflags(e);
+ if (tTd(50, 10))
+ {
+ printf("sendq=");
+ printaddr(e->e_sendqueue, TRUE);
+ }
+ }
+
+ if (LogLevel > 84)
+ sm_syslog(LOG_DEBUG, id,
+ "dropenvelope, e_flags=0x%x, OpMode=%c, pid=%d",
+ e->e_flags, OpMode, getpid());
+
+ /* we must have an id to remove disk files */
+ if (id == NULL)
+ return;
+
+ /* if verify-only mode, we can skip most of this */
+ if (OpMode == MD_VERIFY)
+ goto simpledrop;
+
+ if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
+ logsender(e, NULL);
+ e->e_flags &= ~EF_LOGSENDER;
+
+ /* post statistics */
+ poststats(StatFile);
+
+ /*
+ ** Extract state information from dregs of send list.
+ */
+
+ if (curtime() > e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass])
+ message_timeout = TRUE;
+
+ e->e_flags &= ~EF_QUEUERUN;
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (bitset(QQUEUEUP, q->q_flags) &&
+ bitset(QDONTSEND, q->q_flags))
+ {
+ /* I'm not sure how this happens..... */
+ if (tTd(50, 2))
+ {
+ printf("Bogus flags: ");
+ printaddr(q, FALSE);
+ }
+ q->q_flags &= ~QDONTSEND;
+ }
+ if (!bitset(QBADADDR|QDONTSEND|QSENT, q->q_flags))
+ queueit = TRUE;
+#if XDEBUG
+ else if (bitset(QQUEUEUP, q->q_flags))
+ sm_syslog(LOG_DEBUG, e->e_id,
+ "dropenvelope: q_flags = %x, paddr = %s",
+ q->q_flags, q->q_paddr);
+#endif
+
+ /* see if a notification is needed */
+ if (bitset(QPINGONFAILURE, q->q_flags) &&
+ ((message_timeout && bitset(QQUEUEUP, q->q_flags)) ||
+ bitset(QBADADDR, q->q_flags)))
+ {
+ failure_return = TRUE;
+ if (q->q_owner == NULL && !emptyaddr(&e->e_from))
+ (void) sendtolist(e->e_from.q_paddr, NULLADDR,
+ &e->e_errorqueue, 0, e);
+ }
+ else if (bitset(QPINGONSUCCESS, q->q_flags) &&
+ ((bitset(QSENT, q->q_flags) &&
+ bitnset(M_LOCALMAILER, q->q_mailer->m_flags)) ||
+ bitset(QRELAYED|QEXPANDED|QDELIVERED, q->q_flags)))
+ {
+ success_return = TRUE;
+ }
+ }
+
+ if (e->e_class < 0)
+ e->e_flags |= EF_NO_BODY_RETN;
+
+ /*
+ ** See if the message timed out.
+ */
+
+ if (!queueit)
+ /* nothing to do */ ;
+ else if (message_timeout)
+ {
+ if (failure_return)
+ {
+ (void) snprintf(buf, sizeof buf,
+ "Cannot send message within %s",
+ pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
+ if (e->e_message != NULL)
+ free(e->e_message);
+ e->e_message = newstr(buf);
+ message(buf);
+ e->e_flags |= EF_CLRQUEUE;
+ }
+ fprintf(e->e_xfp, "Message could not be delivered for %s\n",
+ pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
+ fprintf(e->e_xfp, "Message will be deleted from queue\n");
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (!bitset(QBADADDR|QDONTSEND|QSENT, q->q_flags))
+ {
+ q->q_flags |= QBADADDR;
+ q->q_status = "4.4.7";
+ }
+ }
+ }
+ else if (TimeOuts.to_q_warning[e->e_timeoutclass] > 0 &&
+ curtime() > e->e_ctime + TimeOuts.to_q_warning[e->e_timeoutclass])
+ {
+ if (!bitset(EF_WARNING|EF_RESPONSE, e->e_flags) &&
+ e->e_class >= 0 &&
+ e->e_from.q_paddr != NULL &&
+ strcmp(e->e_from.q_paddr, "<>") != 0 &&
+ strncasecmp(e->e_from.q_paddr, "owner-", 6) != 0 &&
+ (strlen(e->e_from.q_paddr) <= (SIZE_T) 8 ||
+ strcasecmp(&e->e_from.q_paddr[strlen(e->e_from.q_paddr) - 8], "-request") != 0))
+ {
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (bitset(QQUEUEUP, q->q_flags) &&
+ bitset(QPINGONDELAY, q->q_flags))
+ {
+ q->q_flags |= QDELAYED;
+ delay_return = TRUE;
+ }
+ }
+ }
+ if (delay_return)
+ {
+ (void) snprintf(buf, sizeof buf,
+ "Warning: could not send message for past %s",
+ pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], FALSE));
+ if (e->e_message != NULL)
+ free(e->e_message);
+ e->e_message = newstr(buf);
+ message(buf);
+ e->e_flags |= EF_WARNING;
+ }
+ fprintf(e->e_xfp,
+ "Warning: message still undelivered after %s\n",
+ pintvl(TimeOuts.to_q_warning[e->e_timeoutclass], FALSE));
+ fprintf(e->e_xfp, "Will keep trying until message is %s old\n",
+ pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
+ }
+
+ if (tTd(50, 2))
+ printf("failure_return=%d delay_return=%d success_return=%d queueit=%d\n",
+ failure_return, delay_return, success_return, queueit);
+
+ /*
+ ** If we had some fatal error, but no addresses are marked as
+ ** bad, mark them _all_ as bad.
+ */
+
+ if (bitset(EF_FATALERRS, e->e_flags) && !failure_return)
+ {
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (!bitset(QDONTSEND, q->q_flags) &&
+ bitset(QPINGONFAILURE, q->q_flags))
+ {
+ failure_return = TRUE;
+ q->q_flags |= QBADADDR;
+ }
+ }
+ }
+
+ /*
+ ** Send back return receipts as requested.
+ */
+
+ if (success_return && !failure_return && !delay_return && fulldrop &&
+ !bitset(PRIV_NORECEIPTS, PrivacyFlags) &&
+ strcmp(e->e_from.q_paddr, "<>") != 0)
+ {
+ auto ADDRESS *rlist = NULL;
+
+ if (tTd(50, 8))
+ printf("dropenvelope(%s): sending return receipt\n", id);
+ e->e_flags |= EF_SENDRECEIPT;
+ (void) sendtolist(e->e_from.q_paddr, NULLADDR, &rlist, 0, e);
+ (void) returntosender("Return receipt", rlist, RTSF_NO_BODY, e);
+ }
+ e->e_flags &= ~EF_SENDRECEIPT;
+
+ /*
+ ** Arrange to send error messages if there are fatal errors.
+ */
+
+ if ((failure_return || delay_return) && e->e_errormode != EM_QUIET)
+ {
+ extern void savemail __P((ENVELOPE *, bool));
+
+ if (tTd(50, 8))
+ printf("dropenvelope(%s): saving mail\n", id);
+ savemail(e, !bitset(EF_NO_BODY_RETN, e->e_flags));
+ }
+
+ /*
+ ** Arrange to send warning messages to postmaster as requested.
+ */
+
+ if ((failure_return || bitset(EF_PM_NOTIFY, e->e_flags)) &&
+ PostMasterCopy != NULL &&
+ !bitset(EF_RESPONSE, e->e_flags) && e->e_class >= 0)
+ {
+ auto ADDRESS *rlist = NULL;
+
+ if (tTd(50, 8))
+ printf("dropenvelope(%s): sending postmaster copy\n", id);
+ (void) sendtolist(PostMasterCopy, NULLADDR, &rlist, 0, e);
+ (void) returntosender(e->e_message, rlist, RTSF_PM_BOUNCE, e);
+ }
+
+ /*
+ ** Instantiate or deinstantiate the queue.
+ */
+
+simpledrop:
+ if (tTd(50, 8))
+ printf("dropenvelope(%s): at simpledrop, queueit=%d\n",
+ id, queueit);
+ if (!queueit || bitset(EF_CLRQUEUE, e->e_flags))
+ {
+ if (tTd(50, 1))
+ {
+ extern void printenvflags __P((ENVELOPE *));
+
+ printf("\n===== Dropping [dq]f%s... queueit=%d, e_flags=",
+ e->e_id, queueit);
+ printenvflags(e);
+ }
+ xunlink(queuename(e, 'd'));
+ xunlink(queuename(e, 'q'));
+
+ if (LogLevel > 10)
+ sm_syslog(LOG_INFO, id, "done");
+ }
+ else if (queueit || !bitset(EF_INQUEUE, e->e_flags))
+ {
+#if QUEUE
+ queueup(e, FALSE);
+#else /* QUEUE */
+ syserr("554 dropenvelope: queueup");
+#endif /* QUEUE */
+ }
+
+ /* now unlock the job */
+ if (tTd(50, 8))
+ printf("dropenvelope(%s): unlocking job\n", id);
+ closexscript(e);
+ unlockqueue(e);
+
+ /* make sure that this envelope is marked unused */
+ if (e->e_dfp != NULL)
+ (void) xfclose(e->e_dfp, "dropenvelope df", e->e_id);
+ e->e_dfp = NULL;
+ e->e_id = NULL;
+ e->e_flags &= ~EF_HAS_DF;
+}
+ /*
+** CLEARENVELOPE -- clear an envelope without unlocking
+**
+** This is normally used by a child process to get a clean
+** envelope without disturbing the parent.
+**
+** Parameters:
+** e -- the envelope to clear.
+** fullclear - if set, the current envelope is total
+** garbage and should be ignored; otherwise,
+** release any resources it may indicate.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Closes files associated with the envelope.
+** Marks the envelope as unallocated.
+*/
+
+void
+clearenvelope(e, fullclear)
+ register ENVELOPE *e;
+ bool fullclear;
+{
+ register HDR *bh;
+ register HDR **nhp;
+ extern ENVELOPE BlankEnvelope;
+
+ if (!fullclear)
+ {
+ /* clear out any file information */
+ if (e->e_xfp != NULL)
+ (void) xfclose(e->e_xfp, "clearenvelope xfp", e->e_id);
+ if (e->e_dfp != NULL)
+ (void) xfclose(e->e_dfp, "clearenvelope dfp", e->e_id);
+ e->e_xfp = e->e_dfp = NULL;
+ }
+
+ /* now clear out the data */
+ STRUCTCOPY(BlankEnvelope, *e);
+ e->e_message = NULL;
+ if (Verbose)
+ e->e_sendmode = SM_DELIVER;
+ bh = BlankEnvelope.e_header;
+ nhp = &e->e_header;
+ while (bh != NULL)
+ {
+ *nhp = (HDR *) xalloc(sizeof *bh);
+ bcopy((char *) bh, (char *) *nhp, sizeof *bh);
+ bh = bh->h_link;
+ nhp = &(*nhp)->h_link;
+ }
+}
+ /*
+** INITSYS -- initialize instantiation of system
+**
+** In Daemon mode, this is done in the child.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Initializes the system macros, some global variables,
+** etc. In particular, the current time in various
+** forms is set.
+*/
+
+void
+initsys(e)
+ register ENVELOPE *e;
+{
+ char cbuf[5]; /* holds hop count */
+ char pbuf[10]; /* holds pid */
+#ifdef TTYNAME
+ static char ybuf[60]; /* holds tty id */
+ register char *p;
+ extern char *ttyname();
+#endif /* TTYNAME */
+ extern void settime __P((ENVELOPE *));
+
+ /*
+ ** Give this envelope a reality.
+ ** I.e., an id, a transcript, and a creation time.
+ */
+
+ openxscript(e);
+ e->e_ctime = curtime();
+
+ /*
+ ** Set OutChannel to something useful if stdout isn't it.
+ ** This arranges that any extra stuff the mailer produces
+ ** gets sent back to the user on error (because it is
+ ** tucked away in the transcript).
+ */
+
+ if (OpMode == MD_DAEMON && bitset(EF_QUEUERUN, e->e_flags) &&
+ e->e_xfp != NULL)
+ OutChannel = e->e_xfp;
+
+ /*
+ ** Set up some basic system macros.
+ */
+
+ /* process id */
+ (void) snprintf(pbuf, sizeof pbuf, "%d", getpid());
+ define('p', newstr(pbuf), e);
+
+ /* hop count */
+ (void) snprintf(cbuf, sizeof cbuf, "%d", e->e_hopcount);
+ define('c', newstr(cbuf), e);
+
+ /* time as integer, unix time, arpa time */
+ settime(e);
+
+#ifdef TTYNAME
+ /* tty name */
+ if (macvalue('y', e) == NULL)
+ {
+ p = ttyname(2);
+ if (p != NULL)
+ {
+ if (strrchr(p, '/') != NULL)
+ p = strrchr(p, '/') + 1;
+ snprintf(ybuf, sizeof ybuf, "%s", p);
+ define('y', ybuf, e);
+ }
+ }
+#endif /* TTYNAME */
+}
+ /*
+** SETTIME -- set the current time.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets the various time macros -- $a, $b, $d, $t.
+*/
+
+void
+settime(e)
+ register ENVELOPE *e;
+{
+ register char *p;
+ auto time_t now;
+ char tbuf[20]; /* holds "current" time */
+ char dbuf[30]; /* holds ctime(tbuf) */
+ register struct tm *tm;
+ extern struct tm *gmtime();
+
+ now = curtime();
+ tm = gmtime(&now);
+ (void) snprintf(tbuf, sizeof tbuf, "%04d%02d%02d%02d%02d", tm->tm_year + 1900,
+ tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+ define('t', newstr(tbuf), e);
+ (void) strcpy(dbuf, ctime(&now));
+ p = strchr(dbuf, '\n');
+ if (p != NULL)
+ *p = '\0';
+ define('d', newstr(dbuf), e);
+ p = arpadate(dbuf);
+ p = newstr(p);
+ if (macvalue('a', e) == NULL)
+ define('a', p, e);
+ define('b', p, e);
+}
+ /*
+** OPENXSCRIPT -- Open transcript file
+**
+** Creates a transcript file for possible eventual mailing or
+** sending back.
+**
+** Parameters:
+** e -- the envelope to create the transcript in/for.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Creates the transcript file.
+*/
+
+#ifndef O_APPEND
+#define O_APPEND 0
+#endif
+
+void
+openxscript(e)
+ register ENVELOPE *e;
+{
+ register char *p;
+ int fd;
+
+ if (e->e_xfp != NULL)
+ return;
+ p = queuename(e, 'x');
+ fd = open(p, O_WRONLY|O_CREAT|O_APPEND, FileMode);
+ if (fd < 0)
+ {
+ syserr("Can't create transcript file %s", p);
+ fd = open("/dev/null", O_WRONLY, 0644);
+ if (fd < 0)
+ syserr("!Can't open /dev/null");
+ }
+ e->e_xfp = fdopen(fd, "a");
+ if (e->e_xfp == NULL)
+ syserr("!Can't create transcript stream %s", p);
+#ifdef HASSETVBUF
+ setvbuf(e->e_xfp, NULL, _IOLBF, 0);
+#else
+ setlinebuf(e->e_xfp);
+#endif
+ if (tTd(46, 9))
+ {
+ printf("openxscript(%s):\n ", p);
+ dumpfd(fileno(e->e_xfp), TRUE, FALSE);
+ }
+}
+ /*
+** CLOSEXSCRIPT -- close the transcript file.
+**
+** Parameters:
+** e -- the envelope containing the transcript to close.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+*/
+
+void
+closexscript(e)
+ register ENVELOPE *e;
+{
+ if (e->e_xfp == NULL)
+ return;
+ (void) xfclose(e->e_xfp, "closexscript", e->e_id);
+ e->e_xfp = NULL;
+}
+ /*
+** SETSENDER -- set the person who this message is from
+**
+** Under certain circumstances allow the user to say who
+** s/he is (using -f or -r). These are:
+** 1. The user's uid is zero (root).
+** 2. The user's login name is in an approved list (typically
+** from a network server).
+** 3. The address the user is trying to claim has a
+** "!" character in it (since #2 doesn't do it for
+** us if we are dialing out for UUCP).
+** A better check to replace #3 would be if the
+** effective uid is "UUCP" -- this would require me
+** to rewrite getpwent to "grab" uucp as it went by,
+** make getname more nasty, do another passwd file
+** scan, or compile the UID of "UUCP" into the code,
+** all of which are reprehensible.
+**
+** Assuming all of these fail, we figure out something
+** ourselves.
+**
+** Parameters:
+** from -- the person we would like to believe this message
+** is from, as specified on the command line.
+** e -- the envelope in which we would like the sender set.
+** delimptr -- if non-NULL, set to the location of the
+** trailing delimiter.
+** delimchar -- the character that will delimit the sender
+** address.
+** internal -- set if this address is coming from an internal
+** source such as an owner alias.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** sets sendmail's notion of who the from person is.
+*/
+
+void
+setsender(from, e, delimptr, delimchar, internal)
+ char *from;
+ register ENVELOPE *e;
+ char **delimptr;
+ int delimchar;
+ bool internal;
+{
+ register char **pvp;
+ char *realname = NULL;
+ register struct passwd *pw;
+ char *bp;
+ char buf[MAXNAME + 2];
+ char pvpbuf[PSBUFSIZE];
+ extern char *FullName;
+
+ if (tTd(45, 1))
+ printf("setsender(%s)\n", from == NULL ? "" : from);
+
+ /*
+ ** Figure out the real user executing us.
+ ** Username can return errno != 0 on non-errors.
+ */
+
+ if (bitset(EF_QUEUERUN, e->e_flags) || OpMode == MD_SMTP ||
+ OpMode == MD_ARPAFTP || OpMode == MD_DAEMON)
+ realname = from;
+ if (realname == NULL || realname[0] == '\0')
+ realname = username();
+
+ if (ConfigLevel < 2)
+ SuprErrs = TRUE;
+
+ e->e_from.q_flags = QBADADDR;
+ if (from == NULL ||
+ parseaddr(from, &e->e_from, RF_COPYALL|RF_SENDERADDR,
+ delimchar, delimptr, e) == NULL ||
+ bitset(QBADADDR, e->e_from.q_flags) ||
+ e->e_from.q_mailer == ProgMailer ||
+ e->e_from.q_mailer == FileMailer ||
+ e->e_from.q_mailer == InclMailer)
+ {
+ /* log garbage addresses for traceback */
+ if (from != NULL && LogLevel > 2)
+ {
+ char *p;
+ char ebuf[MAXNAME * 2 + 2];
+
+ p = macvalue('_', e);
+ if (p == NULL)
+ {
+ char *host = RealHostName;
+
+ if (host == NULL)
+ host = MyHostName;
+ (void) snprintf(ebuf, sizeof ebuf, "%.*s@%.*s",
+ MAXNAME, realname,
+ MAXNAME, host);
+ p = ebuf;
+ }
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "setsender: %s: invalid or unparseable, received from %s",
+ shortenstring(from, 83), p);
+ }
+ if (from != NULL)
+ {
+ if (!bitset(QBADADDR, e->e_from.q_flags))
+ {
+ /* it was a bogus mailer in the from addr */
+ e->e_status = "5.1.7";
+ usrerr("553 Invalid sender address");
+ }
+ SuprErrs = TRUE;
+ }
+ if (from == realname ||
+ parseaddr(from = newstr(realname), &e->e_from,
+ RF_COPYALL|RF_SENDERADDR, ' ', NULL, e) == NULL)
+ {
+ char nbuf[100];
+
+ SuprErrs = TRUE;
+ expand("\201n", nbuf, sizeof nbuf, e);
+ if (parseaddr(from = newstr(nbuf), &e->e_from,
+ RF_COPYALL, ' ', NULL, e) == NULL &&
+ parseaddr(from = "postmaster", &e->e_from,
+ RF_COPYALL, ' ', NULL, e) == NULL)
+ syserr("553 setsender: can't even parse postmaster!");
+ }
+ }
+ else
+ FromFlag = TRUE;
+ e->e_from.q_flags |= QDONTSEND;
+ if (tTd(45, 5))
+ {
+ printf("setsender: QDONTSEND ");
+ printaddr(&e->e_from, FALSE);
+ }
+ SuprErrs = FALSE;
+
+# if USERDB
+ if (bitnset(M_CHECKUDB, e->e_from.q_mailer->m_flags))
+ {
+ register char *p;
+ extern char *udbsender __P((char *));
+
+ p = udbsender(e->e_from.q_user);
+ if (p != NULL)
+ from = p;
+ }
+# endif /* USERDB */
+
+ if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
+ {
+ if (!internal)
+ {
+ /* if the user already given fullname don't redefine */
+ if (FullName == NULL)
+ FullName = macvalue('x', e);
+ if (FullName != NULL && FullName[0] == '\0')
+ FullName = NULL;
+ }
+
+ if (e->e_from.q_user[0] != '\0' &&
+ (pw = sm_getpwnam(e->e_from.q_user)) != NULL)
+ {
+ /*
+ ** Process passwd file entry.
+ */
+
+ /* extract home directory */
+ if (strcmp(pw->pw_dir, "/") == 0)
+ e->e_from.q_home = newstr("");
+ else
+ e->e_from.q_home = newstr(pw->pw_dir);
+ define('z', e->e_from.q_home, e);
+
+ /* extract user and group id */
+ e->e_from.q_uid = pw->pw_uid;
+ e->e_from.q_gid = pw->pw_gid;
+ e->e_from.q_flags |= QGOODUID;
+
+ /* extract full name from passwd file */
+ if (FullName == NULL && pw->pw_gecos != NULL &&
+ strcmp(pw->pw_name, e->e_from.q_user) == 0 &&
+ !internal)
+ {
+ buildfname(pw->pw_gecos, e->e_from.q_user, buf, sizeof buf);
+ if (buf[0] != '\0')
+ FullName = newstr(buf);
+ }
+ }
+ else
+ {
+ e->e_from.q_home = "/no/such/directory";
+ }
+ if (FullName != NULL && !internal)
+ define('x', FullName, e);
+ }
+ else if (!internal && OpMode != MD_DAEMON)
+ {
+ if (e->e_from.q_home == NULL)
+ {
+ e->e_from.q_home = getenv("HOME");
+ if (e->e_from.q_home != NULL &&
+ strcmp(e->e_from.q_home, "/") == 0)
+ e->e_from.q_home++;
+ }
+ e->e_from.q_uid = RealUid;
+ e->e_from.q_gid = RealGid;
+ e->e_from.q_flags |= QGOODUID;
+ }
+
+ /*
+ ** Rewrite the from person to dispose of possible implicit
+ ** links in the net.
+ */
+
+ pvp = prescan(from, delimchar, pvpbuf, sizeof pvpbuf, NULL, NULL);
+ if (pvp == NULL)
+ {
+ /* don't need to give error -- prescan did that already */
+ if (LogLevel > 2)
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "cannot prescan from (%s)",
+ shortenstring(from, MAXSHORTSTR));
+ finis();
+ }
+ (void) rewrite(pvp, 3, 0, e);
+ (void) rewrite(pvp, 1, 0, e);
+ (void) rewrite(pvp, 4, 0, e);
+ bp = buf + 1;
+ cataddr(pvp, NULL, bp, sizeof buf - 2, '\0');
+ if (*bp == '@' && !bitnset(M_NOBRACKET, e->e_from.q_mailer->m_flags))
+ {
+ /* heuristic: route-addr: add angle brackets */
+ strcat(bp, ">");
+ *--bp = '<';
+ }
+ e->e_sender = newstr(bp);
+ define('f', e->e_sender, e);
+
+ /* save the domain spec if this mailer wants it */
+ if (e->e_from.q_mailer != NULL &&
+ bitnset(M_CANONICAL, e->e_from.q_mailer->m_flags))
+ {
+ char **lastat;
+ extern char **copyplist __P((char **, bool));
+
+ /* get rid of any pesky angle brackets */
+ (void) rewrite(pvp, 3, 0, e);
+ (void) rewrite(pvp, 1, 0, e);
+ (void) rewrite(pvp, 4, 0, e);
+
+ /* strip off to the last "@" sign */
+ for (lastat = NULL; *pvp != NULL; pvp++)
+ if (strcmp(*pvp, "@") == 0)
+ lastat = pvp;
+ if (lastat != NULL)
+ {
+ e->e_fromdomain = copyplist(lastat, TRUE);
+ if (tTd(45, 3))
+ {
+ printf("Saving from domain: ");
+ printav(e->e_fromdomain);
+ }
+ }
+ }
+}
+ /*
+** PRINTENVFLAGS -- print envelope flags for debugging
+**
+** Parameters:
+** e -- the envelope with the flags to be printed.
+**
+** Returns:
+** none.
+*/
+
+struct eflags
+{
+ char *ef_name;
+ u_long ef_bit;
+};
+
+struct eflags EnvelopeFlags[] =
+{
+ { "OLDSTYLE", EF_OLDSTYLE },
+ { "INQUEUE", EF_INQUEUE },
+ { "NO_BODY_RETN", EF_NO_BODY_RETN },
+ { "CLRQUEUE", EF_CLRQUEUE },
+ { "SENDRECEIPT", EF_SENDRECEIPT },
+ { "FATALERRS", EF_FATALERRS },
+ { "DELETE_BCC", EF_DELETE_BCC },
+ { "RESPONSE", EF_RESPONSE },
+ { "RESENT", EF_RESENT },
+ { "VRFYONLY", EF_VRFYONLY },
+ { "WARNING", EF_WARNING },
+ { "QUEUERUN", EF_QUEUERUN },
+ { "GLOBALERRS", EF_GLOBALERRS },
+ { "PM_NOTIFY", EF_PM_NOTIFY },
+ { "METOO", EF_METOO },
+ { "LOGSENDER", EF_LOGSENDER },
+ { "NORECEIPT", EF_NORECEIPT },
+ { "HAS8BIT", EF_HAS8BIT },
+ { "NL_NOT_EOL", EF_NL_NOT_EOL },
+ { "CRLF_NOT_EOL", EF_CRLF_NOT_EOL },
+ { "RET_PARAM", EF_RET_PARAM },
+ { "HAS_DF", EF_HAS_DF },
+ { "IS_MIME", EF_IS_MIME },
+ { "DONT_MIME", EF_DONT_MIME },
+ { NULL }
+};
+
+void
+printenvflags(e)
+ register ENVELOPE *e;
+{
+ register struct eflags *ef;
+ bool first = TRUE;
+
+ printf("%lx", e->e_flags);
+ for (ef = EnvelopeFlags; ef->ef_name != NULL; ef++)
+ {
+ if (!bitset(ef->ef_bit, e->e_flags))
+ continue;
+ if (first)
+ printf("<%s", ef->ef_name);
+ else
+ printf(",%s", ef->ef_name);
+ first = FALSE;
+ }
+ if (!first)
+ printf(">\n");
+}
diff --git a/contrib/sendmail/src/err.c b/contrib/sendmail/src/err.c
new file mode 100644
index 0000000..0661395
--- /dev/null
+++ b/contrib/sendmail/src/err.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)err.c 8.74 (Berkeley) 6/4/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include <errno.h>
+
+/*
+** SYSERR -- Print error message.
+**
+** Prints an error message via printf to the diagnostic output.
+**
+** If the first character of the syserr message is `!' it will
+** log this as an ALERT message and exit immediately. This can
+** leave queue files in an indeterminate state, so it should not
+** be used lightly.
+**
+** Parameters:
+** fmt -- the format string. If it does not begin with
+** a three-digit SMTP reply code, either 554 or
+** 451 is assumed depending on whether errno
+** is set.
+** (others) -- parameters
+**
+** Returns:
+** none
+** Through TopFrame if QuickAbort is set.
+**
+** Side Effects:
+** increments Errors.
+** sets ExitStat.
+*/
+
+char MsgBuf[BUFSIZ*2]; /* text of most recent message */
+char HeldMessageBuf[sizeof MsgBuf]; /* for held messages */
+
+extern void putoutmsg __P((char *, bool, bool));
+extern void puterrmsg __P((char *));
+static void fmtmsg __P((char *, const char *, const char *, int, const char *, va_list));
+
+#if NAMED_BIND && !defined(NO_DATA)
+# define NO_DATA NO_ADDRESS
+#endif
+
+void
+/*VARARGS1*/
+#ifdef __STDC__
+syserr(const char *fmt, ...)
+#else
+syserr(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ register char *p;
+ int olderrno = errno;
+ bool panic;
+ char *uname;
+ struct passwd *pw;
+ char ubuf[80];
+ VA_LOCAL_DECL
+
+ panic = *fmt == '!';
+ if (panic)
+ {
+ fmt++;
+ HoldErrs = FALSE;
+ }
+
+ /* format and output the error message */
+ if (olderrno == 0)
+ p = "554";
+ else
+ p = "451";
+ VA_START(fmt);
+ fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
+ VA_END;
+ puterrmsg(MsgBuf);
+
+ /* save this message for mailq printing */
+ if (!panic && CurEnv != NULL)
+ {
+ if (CurEnv->e_message != NULL)
+ free(CurEnv->e_message);
+ CurEnv->e_message = newstr(MsgBuf + 4);
+ }
+
+ /* determine exit status if not already set */
+ if (ExitStat == EX_OK)
+ {
+ if (olderrno == 0)
+ ExitStat = EX_SOFTWARE;
+ else
+ ExitStat = EX_OSERR;
+ if (tTd(54, 1))
+ printf("syserr: ExitStat = %d\n", ExitStat);
+ }
+
+ pw = sm_getpwuid(getuid());
+ if (pw != NULL)
+ uname = pw->pw_name;
+ else
+ {
+ uname = ubuf;
+ snprintf(ubuf, sizeof ubuf, "UID%d", getuid());
+ }
+
+ if (LogLevel > 0)
+ sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
+ CurEnv == NULL ? NOQID : CurEnv->e_id,
+ "SYSERR(%s): %.900s",
+ uname, &MsgBuf[4]);
+ switch (olderrno)
+ {
+ case EBADF:
+ case ENFILE:
+ case EMFILE:
+ case ENOTTY:
+#ifdef EFBIG
+ case EFBIG:
+#endif
+#ifdef ESPIPE
+ case ESPIPE:
+#endif
+#ifdef EPIPE
+ case EPIPE:
+#endif
+#ifdef ENOBUFS
+ case ENOBUFS:
+#endif
+#ifdef ESTALE
+ case ESTALE:
+#endif
+ printopenfds(TRUE);
+ mci_dump_all(TRUE);
+ break;
+ }
+ if (panic)
+ {
+#ifdef XLA
+ xla_all_end();
+#endif
+ if (tTd(0, 1))
+ abort();
+ exit(EX_OSERR);
+ }
+ errno = 0;
+ if (QuickAbort)
+ longjmp(TopFrame, 2);
+}
+ /*
+** USRERR -- Signal user error.
+**
+** This is much like syserr except it is for user errors.
+**
+** Parameters:
+** fmt -- the format string. If it does not begin with
+** a three-digit SMTP reply code, 501 is assumed.
+** (others) -- printf strings
+**
+** Returns:
+** none
+** Through TopFrame if QuickAbort is set.
+**
+** Side Effects:
+** increments Errors.
+*/
+
+/*VARARGS1*/
+void
+#ifdef __STDC__
+usrerr(const char *fmt, ...)
+#else
+usrerr(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ VA_LOCAL_DECL
+
+ if (SuprErrs)
+ return;
+
+ VA_START(fmt);
+ fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
+ VA_END;
+
+ /* save this message for mailq printing */
+ switch (MsgBuf[0])
+ {
+ case '4':
+ case '8':
+ if (CurEnv->e_message != NULL)
+ break;
+
+ /* fall through.... */
+
+ case '5':
+ case '6':
+ if (CurEnv->e_message != NULL)
+ free(CurEnv->e_message);
+ if (MsgBuf[0] == '6')
+ {
+ char buf[MAXLINE];
+
+ snprintf(buf, sizeof buf, "Postmaster warning: %.*s",
+ sizeof buf - 22, MsgBuf + 4);
+ CurEnv->e_message = newstr(buf);
+ }
+ else
+ {
+ CurEnv->e_message = newstr(MsgBuf + 4);
+ }
+ break;
+ }
+
+ puterrmsg(MsgBuf);
+
+ if (LogLevel > 3 && LogUsrErrs)
+ sm_syslog(LOG_NOTICE, CurEnv->e_id,
+ "%.900s",
+ &MsgBuf[4]);
+
+ if (QuickAbort)
+ longjmp(TopFrame, 1);
+}
+ /*
+** MESSAGE -- print message (not necessarily an error)
+**
+** Parameters:
+** msg -- the message (printf fmt) -- it can begin with
+** an SMTP reply code. If not, 050 is assumed.
+** (others) -- printf arguments
+**
+** Returns:
+** none
+**
+** Side Effects:
+** none.
+*/
+
+/*VARARGS1*/
+void
+#ifdef __STDC__
+message(const char *msg, ...)
+#else
+message(msg, va_alist)
+ const char *msg;
+ va_dcl
+#endif
+{
+ VA_LOCAL_DECL
+
+ errno = 0;
+ VA_START(msg);
+ fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
+ VA_END;
+ putoutmsg(MsgBuf, FALSE, FALSE);
+
+ /* save this message for mailq printing */
+ switch (MsgBuf[0])
+ {
+ case '4':
+ case '8':
+ if (CurEnv->e_message != NULL)
+ break;
+ /* fall through.... */
+
+ case '5':
+ if (CurEnv->e_message != NULL)
+ free(CurEnv->e_message);
+ CurEnv->e_message = newstr(MsgBuf + 4);
+ break;
+ }
+}
+ /*
+** NMESSAGE -- print message (not necessarily an error)
+**
+** Just like "message" except it never puts the to... tag on.
+**
+** Parameters:
+** msg -- the message (printf fmt) -- if it begins
+** with a three digit SMTP reply code, that is used,
+** otherwise 050 is assumed.
+** (others) -- printf arguments
+**
+** Returns:
+** none
+**
+** Side Effects:
+** none.
+*/
+
+/*VARARGS1*/
+void
+#ifdef __STDC__
+nmessage(const char *msg, ...)
+#else
+nmessage(msg, va_alist)
+ const char *msg;
+ va_dcl
+#endif
+{
+ VA_LOCAL_DECL
+
+ errno = 0;
+ VA_START(msg);
+ fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
+ VA_END;
+ putoutmsg(MsgBuf, FALSE, FALSE);
+
+ /* save this message for mailq printing */
+ switch (MsgBuf[0])
+ {
+ case '4':
+ case '8':
+ if (CurEnv->e_message != NULL)
+ break;
+ /* fall through.... */
+
+ case '5':
+ if (CurEnv->e_message != NULL)
+ free(CurEnv->e_message);
+ CurEnv->e_message = newstr(MsgBuf + 4);
+ break;
+ }
+}
+ /*
+** PUTOUTMSG -- output error message to transcript and channel
+**
+** Parameters:
+** msg -- message to output (in SMTP format).
+** holdmsg -- if TRUE, don't output a copy of the message to
+** our output channel.
+** heldmsg -- if TRUE, this is a previously held message;
+** don't log it to the transcript file.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Outputs msg to the transcript.
+** If appropriate, outputs it to the channel.
+** Deletes SMTP reply code number as appropriate.
+*/
+
+void
+putoutmsg(msg, holdmsg, heldmsg)
+ char *msg;
+ bool holdmsg;
+ bool heldmsg;
+{
+ char msgcode = msg[0];
+
+ /* display for debugging */
+ if (tTd(54, 8))
+ printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
+ heldmsg ? " (held)" : "");
+
+ /* map warnings to something SMTP can handle */
+ if (msgcode == '6')
+ msg[0] = '5';
+ else if (msgcode == '8')
+ msg[0] = '4';
+
+ /* output to transcript if serious */
+ if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
+ strchr("45", msg[0]) != NULL)
+ fprintf(CurEnv->e_xfp, "%s\n", msg);
+
+ if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
+ sm_syslog(LOG_INFO, CurEnv->e_id,
+ "--> %s%s",
+ msg, holdmsg ? " (held)" : "");
+
+ if (msgcode == '8')
+ msg[0] = '0';
+
+ /* output to channel if appropriate */
+ if (!Verbose && msg[0] == '0')
+ return;
+ if (holdmsg)
+ {
+ /* save for possible future display */
+ msg[0] = msgcode;
+ snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg);
+ return;
+ }
+
+ (void) fflush(stdout);
+
+ if (OutChannel == NULL)
+ return;
+
+ /* if DisConnected, OutChannel now points to the transcript */
+ if (!DisConnected &&
+ (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
+ fprintf(OutChannel, "%s\r\n", msg);
+ else
+ fprintf(OutChannel, "%s\n", &msg[4]);
+ if (TrafficLogFile != NULL)
+ fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(),
+ (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
+ if (msg[3] == ' ')
+ (void) fflush(OutChannel);
+ if (!ferror(OutChannel) || DisConnected)
+ return;
+
+ /*
+ ** Error on output -- if reporting lost channel, just ignore it.
+ ** Also, ignore errors from QUIT response (221 message) -- some
+ ** rude servers don't read result.
+ */
+
+ if (InChannel == NULL || feof(InChannel) || ferror(InChannel) ||
+ strncmp(msg, "221", 3) == 0)
+ return;
+
+ /* can't call syserr, 'cause we are using MsgBuf */
+ HoldErrs = TRUE;
+ if (LogLevel > 0)
+ sm_syslog(LOG_CRIT, CurEnv->e_id,
+ "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
+ CurHostName == NULL ? "NO-HOST" : CurHostName,
+ shortenstring(msg, MAXSHORTSTR), errstring(errno));
+}
+ /*
+** PUTERRMSG -- like putoutmsg, but does special processing for error messages
+**
+** Parameters:
+** msg -- the message to output.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets the fatal error bit in the envelope as appropriate.
+*/
+
+void
+puterrmsg(msg)
+ char *msg;
+{
+ char msgcode = msg[0];
+
+ /* output the message as usual */
+ putoutmsg(msg, HoldErrs, FALSE);
+
+ /* be careful about multiple error messages */
+ if (OnlyOneError)
+ HoldErrs = TRUE;
+
+ /* signal the error */
+ Errors++;
+
+ if (CurEnv == NULL)
+ return;
+
+ if (msgcode == '6')
+ {
+ /* notify the postmaster */
+ CurEnv->e_flags |= EF_PM_NOTIFY;
+ }
+ else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
+ {
+ /* mark long-term fatal errors */
+ CurEnv->e_flags |= EF_FATALERRS;
+ }
+}
+ /*
+** FMTMSG -- format a message into buffer.
+**
+** Parameters:
+** eb -- error buffer to get result.
+** to -- the recipient tag for this message.
+** num -- arpanet error number.
+** en -- the error number to display.
+** fmt -- format of string.
+** a, b, c, d, e -- arguments.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+*/
+
+static void
+fmtmsg(eb, to, num, eno, fmt, ap)
+ register char *eb;
+ const char *to;
+ const char *num;
+ int eno;
+ const char *fmt;
+ va_list ap;
+{
+ char del;
+ int l;
+ int spaceleft = sizeof MsgBuf;
+
+ /* output the reply code */
+ if (isascii(fmt[0]) && isdigit(fmt[0]) &&
+ isascii(fmt[1]) && isdigit(fmt[1]) &&
+ isascii(fmt[2]) && isdigit(fmt[2]))
+ {
+ num = fmt;
+ fmt += 4;
+ }
+ if (num[3] == '-')
+ del = '-';
+ else
+ del = ' ';
+ (void) snprintf(eb, spaceleft, "%3.3s%c", num, del);
+ eb += 4;
+ spaceleft -= 4;
+
+ /* output the file name and line number */
+ if (FileName != NULL)
+ {
+ (void) snprintf(eb, spaceleft, "%s: line %d: ",
+ shortenstring(FileName, 83), LineNumber);
+ eb += (l = strlen(eb));
+ spaceleft -= l;
+ }
+
+ /* output the "to" person */
+ if (to != NULL && to[0] != '\0' &&
+ strncmp(num, "551", 3) != 0 &&
+ strncmp(num, "251", 3) != 0)
+ {
+ (void) snprintf(eb, spaceleft, "%s... ",
+ shortenstring(to, MAXSHORTSTR));
+ spaceleft -= strlen(eb);
+ while (*eb != '\0')
+ *eb++ &= 0177;
+ }
+
+ /* output the message */
+ (void) vsnprintf(eb, spaceleft, fmt, ap);
+ spaceleft -= strlen(eb);
+ while (*eb != '\0')
+ *eb++ &= 0177;
+
+ /* output the error code, if any */
+ if (eno != 0)
+ (void) snprintf(eb, spaceleft, ": %s", errstring(eno));
+}
+ /*
+** BUFFER_ERRORS -- arrange to buffer future error messages
+**
+** Parameters:
+** none
+**
+** Returns:
+** none.
+*/
+
+void
+buffer_errors()
+{
+ HeldMessageBuf[0] = '\0';
+ HoldErrs = TRUE;
+}
+ /*
+** FLUSH_ERRORS -- flush the held error message buffer
+**
+** Parameters:
+** print -- if set, print the message, otherwise just
+** delete it.
+**
+** Returns:
+** none.
+*/
+
+void
+flush_errors(print)
+ bool print;
+{
+ if (print && HeldMessageBuf[0] != '\0')
+ putoutmsg(HeldMessageBuf, FALSE, TRUE);
+ HeldMessageBuf[0] = '\0';
+ HoldErrs = FALSE;
+}
+ /*
+** ERRSTRING -- return string description of error code
+**
+** Parameters:
+** errnum -- the error number to translate
+**
+** Returns:
+** A string description of errnum.
+**
+** Side Effects:
+** none.
+*/
+
+const char *
+errstring(errnum)
+ int errnum;
+{
+ char *dnsmsg;
+ char *bp;
+ static char buf[MAXLINE];
+# if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+# endif
+# if SMTP
+ extern char *SmtpPhase;
+# endif /* SMTP */
+
+ /*
+ ** Handle special network error codes.
+ **
+ ** These are 4.2/4.3bsd specific; they should be in daemon.c.
+ */
+
+ dnsmsg = NULL;
+ switch (errnum)
+ {
+# if defined(DAEMON) && defined(ETIMEDOUT)
+ case ETIMEDOUT:
+ case ECONNRESET:
+ bp = buf;
+#if HASSTRERROR
+ snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum));
+#else
+ if (errnum >= 0 && errnum < sys_nerr)
+ snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]);
+ else
+ snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum);
+#endif
+ bp += strlen(bp);
+ if (CurHostName != NULL)
+ {
+ if (errnum == ETIMEDOUT)
+ {
+ snprintf(bp, SPACELEFT(buf, bp), " with ");
+ bp += strlen(bp);
+ }
+ else
+ {
+ bp = buf;
+ snprintf(bp, SPACELEFT(buf, bp),
+ "Connection reset by ");
+ bp += strlen(bp);
+ }
+ snprintf(bp, SPACELEFT(buf, bp), "%s",
+ shortenstring(CurHostName, MAXSHORTSTR));
+ bp += strlen(buf);
+ }
+ if (SmtpPhase != NULL)
+ {
+ snprintf(bp, SPACELEFT(buf, bp), " during %s",
+ SmtpPhase);
+ }
+ return (buf);
+
+ case EHOSTDOWN:
+ if (CurHostName == NULL)
+ break;
+ (void) snprintf(buf, sizeof buf, "Host %s is down",
+ shortenstring(CurHostName, MAXSHORTSTR));
+ return (buf);
+
+ case ECONNREFUSED:
+ if (CurHostName == NULL)
+ break;
+ (void) snprintf(buf, sizeof buf, "Connection refused by %s",
+ shortenstring(CurHostName, MAXSHORTSTR));
+ return (buf);
+# endif
+
+# if NAMED_BIND
+ case HOST_NOT_FOUND + E_DNSBASE:
+ dnsmsg = "host not found";
+ break;
+
+ case TRY_AGAIN + E_DNSBASE:
+ dnsmsg = "host name lookup failure";
+ break;
+
+ case NO_RECOVERY + E_DNSBASE:
+ dnsmsg = "non-recoverable error";
+ break;
+
+ case NO_DATA + E_DNSBASE:
+ dnsmsg = "no data known";
+ break;
+# endif
+
+ case EPERM:
+ /* SunOS gives "Not owner" -- this is the POSIX message */
+ return "Operation not permitted";
+
+ /*
+ ** Error messages used internally in sendmail.
+ */
+
+ case E_SM_OPENTIMEOUT:
+ return "Timeout on file open";
+
+ case E_SM_NOSLINK:
+ return "Symbolic links not allowed";
+
+ case E_SM_NOHLINK:
+ return "Hard links not allowed";
+
+ case E_SM_REGONLY:
+ return "Regular files only";
+
+ case E_SM_ISEXEC:
+ return "Executable files not allowed";
+
+ case E_SM_WWDIR:
+ return "World writable directory";
+
+ case E_SM_GWDIR:
+ return "Group writable directory";
+
+ case E_SM_FILECHANGE:
+ return "File changed after open";
+
+ case E_SM_WWFILE:
+ return "World writable file";
+
+ case E_SM_GWFILE:
+ return "Group writable file";
+ }
+
+ if (dnsmsg != NULL)
+ {
+ bp = buf;
+ strcpy(bp, "Name server: ");
+ bp += strlen(bp);
+ if (CurHostName != NULL)
+ {
+ snprintf(bp, SPACELEFT(buf, bp), "%s: ",
+ shortenstring(CurHostName, MAXSHORTSTR));
+ bp += strlen(bp);
+ }
+ snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg);
+ return buf;
+ }
+
+#if HASSTRERROR
+ return strerror(errnum);
+#else
+ if (errnum > 0 && errnum < sys_nerr)
+ return (sys_errlist[errnum]);
+
+ (void) snprintf(buf, sizeof buf, "Error %d", errnum);
+ return (buf);
+#endif
+}
diff --git a/contrib/sendmail/src/headers.c b/contrib/sendmail/src/headers.c
new file mode 100644
index 0000000..a04f59e
--- /dev/null
+++ b/contrib/sendmail/src/headers.c
@@ -0,0 +1,1570 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)headers.c 8.127 (Berkeley) 6/4/98";
+#endif /* not lint */
+
+# include <errno.h>
+# include "sendmail.h"
+
+/*
+** SETUPHEADERS -- initialize headers in symbol table
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+*/
+
+void
+setupheaders()
+{
+ struct hdrinfo *hi;
+ STAB *s;
+
+ for (hi = HdrInfo; hi->hi_field != NULL; hi++)
+ {
+ s = stab(hi->hi_field, ST_HEADER, ST_ENTER);
+ s->s_header.hi_flags = hi->hi_flags;
+ s->s_header.hi_ruleset = NULL;
+ }
+}
+ /*
+** CHOMPHEADER -- process and save a header line.
+**
+** Called by collect and by readcf to deal with header lines.
+**
+** Parameters:
+** line -- header as a text line.
+** def -- if set, this is a default value.
+** hdrp -- a pointer to the place to save the header.
+** e -- the envelope including this header.
+**
+** Returns:
+** flags for this header.
+**
+** Side Effects:
+** The header is saved on the header list.
+** Contents of 'line' are destroyed.
+*/
+
+struct hdrinfo NormalHeader = { NULL, 0, NULL };
+
+int
+chompheader(line, def, hdrp, e)
+ char *line;
+ bool def;
+ HDR **hdrp;
+ register ENVELOPE *e;
+{
+ register char *p;
+ register HDR *h;
+ HDR **hp;
+ char *fname;
+ char *fvalue;
+ bool cond = FALSE;
+ bool headeronly;
+ STAB *s;
+ struct hdrinfo *hi;
+ BITMAP mopts;
+
+ if (tTd(31, 6))
+ {
+ printf("chompheader: ");
+ xputs(line);
+ printf("\n");
+ }
+
+ headeronly = hdrp != NULL;
+ if (!headeronly)
+ hdrp = &e->e_header;
+
+ /* strip off options */
+ clrbitmap(mopts);
+ p = line;
+ if (*p == '?')
+ {
+ /* have some */
+ register char *q = strchr(p + 1, *p);
+
+ if (q != NULL)
+ {
+ *q++ = '\0';
+ while (*++p != '\0')
+ setbitn(*p, mopts);
+ p = q;
+ }
+ else
+ syserr("553 header syntax error, line \"%s\"", line);
+ cond = TRUE;
+ }
+
+ /* find canonical name */
+ fname = p;
+ while (isascii(*p) && isgraph(*p) && *p != ':')
+ p++;
+ fvalue = p;
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p++ != ':' || fname == fvalue)
+ {
+ syserr("553 header syntax error, line \"%s\"", line);
+ return 0;
+ }
+ *fvalue = '\0';
+ fvalue = p;
+
+ /* strip field value on front */
+ if (*fvalue == ' ')
+ fvalue++;
+
+ /* security scan: long field names are end-of-header */
+ if (strlen(fname) > 100)
+ return H_EOH;
+
+ /* check to see if it represents a ruleset call */
+ if (def)
+ {
+ char hbuf[50];
+
+ (void) expand(fvalue, hbuf, sizeof hbuf, e);
+ for (p = hbuf; isascii(*p) && isspace(*p); )
+ p++;
+ if ((*p++ & 0377) == CALLSUBR)
+ {
+ auto char *endp;
+
+ if (strtorwset(p, &endp, ST_ENTER) > 0)
+ {
+ *endp = '\0';
+ s = stab(fname, ST_HEADER, ST_ENTER);
+ s->s_header.hi_ruleset = newstr(p);
+ }
+ return 0;
+ }
+ }
+
+ /* see if it is a known type */
+ s = stab(fname, ST_HEADER, ST_FIND);
+ if (s != NULL)
+ hi = &s->s_header;
+ else
+ hi = &NormalHeader;
+
+ if (tTd(31, 9))
+ {
+ if (s == NULL)
+ printf("no header flags match\n");
+ else
+ printf("header match, flags=%x, ruleset=%s\n",
+ hi->hi_flags,
+ hi->hi_ruleset == NULL ? "<NULL>" : hi->hi_ruleset);
+ }
+
+ /* see if this is a resent message */
+ if (!def && !headeronly && bitset(H_RESENT, hi->hi_flags))
+ e->e_flags |= EF_RESENT;
+
+ /* if this is an Errors-To: header keep track of it now */
+ if (UseErrorsTo && !def && !headeronly &&
+ bitset(H_ERRORSTO, hi->hi_flags))
+ (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e);
+
+ /* if this means "end of header" quit now */
+ if (!headeronly && bitset(H_EOH, hi->hi_flags))
+ return hi->hi_flags;
+
+ /*
+ ** Horrible hack to work around problem with Lotus Notes SMTP
+ ** mail gateway, which generates From: headers with newlines in
+ ** them and the <address> on the second line. Although this is
+ ** legal RFC 822, many MUAs don't handle this properly and thus
+ ** never find the actual address.
+ */
+
+ if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader)
+ {
+ while ((p = strchr(fvalue, '\n')) != NULL)
+ *p = ' ';
+ }
+
+ /*
+ ** If there is a check ruleset, verify it against the header.
+ */
+
+ if (!def && hi->hi_ruleset != NULL)
+ (void) rscheck(hi->hi_ruleset, fvalue, NULL, e);
+
+ /*
+ ** Drop explicit From: if same as what we would generate.
+ ** This is to make MH (which doesn't always give a full name)
+ ** insert the full name information in all circumstances.
+ */
+
+ p = "resent-from";
+ if (!bitset(EF_RESENT, e->e_flags))
+ p += 7;
+ if (!def && !headeronly && !bitset(EF_QUEUERUN, e->e_flags) &&
+ strcasecmp(fname, p) == 0)
+ {
+ if (tTd(31, 2))
+ {
+ printf("comparing header from (%s) against default (%s or %s)\n",
+ fvalue, e->e_from.q_paddr, e->e_from.q_user);
+ }
+ if (e->e_from.q_paddr != NULL &&
+ (strcmp(fvalue, e->e_from.q_paddr) == 0 ||
+ strcmp(fvalue, e->e_from.q_user) == 0))
+ return hi->hi_flags;
+ }
+
+ /* delete default value for this header */
+ for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link)
+ {
+ if (strcasecmp(fname, h->h_field) == 0 &&
+ bitset(H_DEFAULT, h->h_flags) &&
+ !bitset(H_FORCE, h->h_flags))
+ {
+ h->h_value = NULL;
+ if (!cond)
+ {
+ /* copy conditions from default case */
+ bcopy((char *)h->h_mflags, (char *)mopts,
+ sizeof mopts);
+ }
+ }
+ }
+
+ /* create a new node */
+ h = (HDR *) xalloc(sizeof *h);
+ h->h_field = newstr(fname);
+ h->h_value = newstr(fvalue);
+ h->h_link = NULL;
+ bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts);
+ *hp = h;
+ h->h_flags = hi->hi_flags;
+
+ /* strip EOH flag if parsing MIME headers */
+ if (headeronly)
+ h->h_flags &= ~H_EOH;
+ if (def)
+ h->h_flags |= H_DEFAULT;
+ if (cond)
+ h->h_flags |= H_CHECK;
+
+ /* hack to see if this is a new format message */
+ if (!def && !headeronly && bitset(H_RCPT|H_FROM, h->h_flags) &&
+ (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL ||
+ strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL))
+ {
+ e->e_flags &= ~EF_OLDSTYLE;
+ }
+
+ return h->h_flags;
+}
+ /*
+** ADDHEADER -- add a header entry to the end of the queue.
+**
+** This bypasses the special checking of chompheader.
+**
+** Parameters:
+** field -- the name of the header field.
+** value -- the value of the field.
+** hp -- an indirect pointer to the header structure list.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** adds the field on the list of headers for this envelope.
+*/
+
+void
+addheader(field, value, hdrlist)
+ char *field;
+ char *value;
+ HDR **hdrlist;
+{
+ register HDR *h;
+ STAB *s;
+ HDR **hp;
+
+ /* find info struct */
+ s = stab(field, ST_HEADER, ST_FIND);
+
+ /* find current place in list -- keep back pointer? */
+ for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link)
+ {
+ if (strcasecmp(field, h->h_field) == 0)
+ break;
+ }
+
+ /* allocate space for new header */
+ h = (HDR *) xalloc(sizeof *h);
+ h->h_field = field;
+ h->h_value = newstr(value);
+ h->h_link = *hp;
+ h->h_flags = H_DEFAULT;
+ if (s != NULL)
+ h->h_flags |= s->s_header.hi_flags;
+ clrbitmap(h->h_mflags);
+ *hp = h;
+}
+ /*
+** HVALUE -- return value of a header.
+**
+** Only "real" fields (i.e., ones that have not been supplied
+** as a default) are used.
+**
+** Parameters:
+** field -- the field name.
+** header -- the header list.
+**
+** Returns:
+** pointer to the value part.
+** NULL if not found.
+**
+** Side Effects:
+** none.
+*/
+
+char *
+hvalue(field, header)
+ char *field;
+ HDR *header;
+{
+ register HDR *h;
+
+ for (h = header; h != NULL; h = h->h_link)
+ {
+ if (!bitset(H_DEFAULT, h->h_flags) &&
+ strcasecmp(h->h_field, field) == 0)
+ return (h->h_value);
+ }
+ return (NULL);
+}
+ /*
+** ISHEADER -- predicate telling if argument is a header.
+**
+** A line is a header if it has a single word followed by
+** optional white space followed by a colon.
+**
+** Header fields beginning with two dashes, although technically
+** permitted by RFC822, are automatically rejected in order
+** to make MIME work out. Without this we could have a technically
+** legal header such as ``--"foo:bar"'' that would also be a legal
+** MIME separator.
+**
+** Parameters:
+** h -- string to check for possible headerness.
+**
+** Returns:
+** TRUE if h is a header.
+** FALSE otherwise.
+**
+** Side Effects:
+** none.
+*/
+
+bool
+isheader(h)
+ char *h;
+{
+ register char *s = h;
+
+ if (s[0] == '-' && s[1] == '-')
+ return FALSE;
+
+ while (*s > ' ' && *s != ':' && *s != '\0')
+ s++;
+
+ if (h == s)
+ return FALSE;
+
+ /* following technically violates RFC822 */
+ while (isascii(*s) && isspace(*s))
+ s++;
+
+ return (*s == ':');
+}
+ /*
+** EATHEADER -- run through the stored header and extract info.
+**
+** Parameters:
+** e -- the envelope to process.
+** full -- if set, do full processing (e.g., compute
+** message priority). This should not be set
+** when reading a queue file because some info
+** needed to compute the priority is wrong.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets a bunch of global variables from information
+** in the collected header.
+** Aborts the message if the hop count is exceeded.
+*/
+
+void
+eatheader(e, full)
+ register ENVELOPE *e;
+ bool full;
+{
+ register HDR *h;
+ register char *p;
+ int hopcnt = 0;
+ char *msgid;
+ char buf[MAXLINE];
+ extern int priencode __P((char *));
+
+ /*
+ ** Set up macros for possible expansion in headers.
+ */
+
+ define('f', e->e_sender, e);
+ define('g', e->e_sender, e);
+ if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0')
+ define('u', e->e_origrcpt, e);
+ else
+ define('u', NULL, e);
+
+ /* full name of from person */
+ p = hvalue("full-name", e->e_header);
+ if (p != NULL)
+ {
+ extern bool rfc822_string __P((char *));
+
+ if (!rfc822_string(p))
+ {
+ extern char *addquotes __P((char *));
+
+ /*
+ ** Quote a full name with special characters
+ ** as a comment so crackaddr() doesn't destroy
+ ** the name portion of the address.
+ */
+ p = addquotes(p);
+ }
+ define('x', p, e);
+ }
+
+ if (tTd(32, 1))
+ printf("----- collected header -----\n");
+ msgid = NULL;
+ for (h = e->e_header; h != NULL; h = h->h_link)
+ {
+ if (tTd(32, 1))
+ printf("%s: ", h->h_field);
+ if (h->h_value == NULL)
+ {
+ if (tTd(32, 1))
+ printf("<NULL>\n");
+ continue;
+ }
+
+ /* do early binding */
+ if (bitset(H_DEFAULT, h->h_flags))
+ {
+ if (tTd(32, 1))
+ {
+ printf("(");
+ xputs(h->h_value);
+ printf(") ");
+ }
+ expand(h->h_value, buf, sizeof buf, e);
+ if (buf[0] != '\0')
+ {
+ if (bitset(H_FROM, h->h_flags))
+ {
+ extern char *crackaddr __P((char *));
+
+ expand(crackaddr(buf), buf, sizeof buf, e);
+ }
+ h->h_value = newstr(buf);
+ h->h_flags &= ~H_DEFAULT;
+ }
+ }
+
+ if (tTd(32, 1))
+ {
+ xputs(h->h_value);
+ printf("\n");
+ }
+
+ /* count the number of times it has been processed */
+ if (bitset(H_TRACE, h->h_flags))
+ hopcnt++;
+
+ /* send to this person if we so desire */
+ if (GrabTo && bitset(H_RCPT, h->h_flags) &&
+ !bitset(H_DEFAULT, h->h_flags) &&
+ (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags)))
+ {
+#if 0
+ int saveflags = e->e_flags;
+#endif
+
+ (void) sendtolist(h->h_value, NULLADDR,
+ &e->e_sendqueue, 0, e);
+
+#if 0
+ /*
+ ** Change functionality so a fatal error on an
+ ** address doesn't affect the entire envelope.
+ */
+
+ /* delete fatal errors generated by this address */
+ if (!bitset(EF_FATALERRS, saveflags))
+ e->e_flags &= ~EF_FATALERRS;
+#endif
+ }
+
+ /* save the message-id for logging */
+ p = "resent-message-id";
+ if (!bitset(EF_RESENT, e->e_flags))
+ p += 7;
+ if (strcasecmp(h->h_field, p) == 0)
+ {
+ msgid = h->h_value;
+ while (isascii(*msgid) && isspace(*msgid))
+ msgid++;
+ }
+ }
+ if (tTd(32, 1))
+ printf("----------------------------\n");
+
+ /* if we are just verifying (that is, sendmail -t -bv), drop out now */
+ if (OpMode == MD_VERIFY)
+ return;
+
+ /* store hop count */
+ if (hopcnt > e->e_hopcount)
+ e->e_hopcount = hopcnt;
+
+ /* message priority */
+ p = hvalue("precedence", e->e_header);
+ if (p != NULL)
+ e->e_class = priencode(p);
+ if (e->e_class < 0)
+ e->e_timeoutclass = TOC_NONURGENT;
+ else if (e->e_class > 0)
+ e->e_timeoutclass = TOC_URGENT;
+ if (full)
+ {
+ e->e_msgpriority = e->e_msgsize
+ - e->e_class * WkClassFact
+ + e->e_nrcpts * WkRecipFact;
+ }
+
+ /* message timeout priority */
+ p = hvalue("priority", e->e_header);
+ if (p != NULL)
+ {
+ /* (this should be in the configuration file) */
+ if (strcasecmp(p, "urgent") == 0)
+ e->e_timeoutclass = TOC_URGENT;
+ else if (strcasecmp(p, "normal") == 0)
+ e->e_timeoutclass = TOC_NORMAL;
+ else if (strcasecmp(p, "non-urgent") == 0)
+ e->e_timeoutclass = TOC_NONURGENT;
+ }
+
+ /* date message originated */
+ p = hvalue("posted-date", e->e_header);
+ if (p == NULL)
+ p = hvalue("date", e->e_header);
+ if (p != NULL)
+ define('a', p, e);
+
+ /* check to see if this is a MIME message */
+ if ((e->e_bodytype != NULL &&
+ strcasecmp(e->e_bodytype, "8BITMIME") == 0) ||
+ hvalue("MIME-Version", e->e_header) != NULL)
+ {
+ e->e_flags |= EF_IS_MIME;
+ if (HasEightBits)
+ e->e_bodytype = "8BITMIME";
+ }
+ else if ((p = hvalue("Content-Type", e->e_header)) != NULL)
+ {
+ /* this may be an RFC 1049 message */
+ p = strpbrk(p, ";/");
+ if (p == NULL || *p == ';')
+ {
+ /* yep, it is */
+ e->e_flags |= EF_DONT_MIME;
+ }
+ }
+
+ /*
+ ** From person in antiquated ARPANET mode
+ ** required by UK Grey Book e-mail gateways (sigh)
+ */
+
+ if (OpMode == MD_ARPAFTP)
+ {
+ register struct hdrinfo *hi;
+
+ for (hi = HdrInfo; hi->hi_field != NULL; hi++)
+ {
+ if (bitset(H_FROM, hi->hi_flags) &&
+ (!bitset(H_RESENT, hi->hi_flags) ||
+ bitset(EF_RESENT, e->e_flags)) &&
+ (p = hvalue(hi->hi_field, e->e_header)) != NULL)
+ break;
+ }
+ if (hi->hi_field != NULL)
+ {
+ if (tTd(32, 2))
+ printf("eatheader: setsender(*%s == %s)\n",
+ hi->hi_field, p);
+ setsender(p, e, NULL, '\0', TRUE);
+ }
+ }
+
+ /*
+ ** Log collection information.
+ */
+
+ if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
+ logsender(e, msgid);
+ e->e_flags &= ~EF_LOGSENDER;
+}
+ /*
+** LOGSENDER -- log sender information
+**
+** Parameters:
+** e -- the envelope to log
+** msgid -- the message id
+**
+** Returns:
+** none
+*/
+
+void
+logsender(e, msgid)
+ register ENVELOPE *e;
+ char *msgid;
+{
+ char *name;
+ register char *sbp;
+ register char *p;
+ int l;
+ char hbuf[MAXNAME + 1];
+ char sbuf[MAXLINE + 1];
+ char mbuf[MAXNAME + 1];
+
+ /* don't allow newlines in the message-id */
+ if (msgid != NULL)
+ {
+ l = strlen(msgid);
+ if (l > sizeof mbuf - 1)
+ l = sizeof mbuf - 1;
+ bcopy(msgid, mbuf, l);
+ mbuf[l] = '\0';
+ p = mbuf;
+ while ((p = strchr(p, '\n')) != NULL)
+ *p++ = ' ';
+ }
+
+ if (bitset(EF_RESPONSE, e->e_flags))
+ name = "[RESPONSE]";
+ else if ((name = macvalue('_', e)) != NULL)
+ ;
+ else if (RealHostName == NULL)
+ name = "localhost";
+ else if (RealHostName[0] == '[')
+ name = RealHostName;
+ else
+ {
+ name = hbuf;
+ (void) snprintf(hbuf, sizeof hbuf, "%.80s", RealHostName);
+ if (RealHostAddr.sa.sa_family != 0)
+ {
+ p = &hbuf[strlen(hbuf)];
+ (void) snprintf(p, SPACELEFT(hbuf, p), " (%.100s)",
+ anynet_ntoa(&RealHostAddr));
+ }
+ }
+
+ /* some versions of syslog only take 5 printf args */
+# if (SYSLOG_BUFSIZE) >= 256
+ sbp = sbuf;
+ snprintf(sbp, SPACELEFT(sbuf, sbp),
+ "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d",
+ e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr,
+ e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts);
+ sbp += strlen(sbp);
+ if (msgid != NULL)
+ {
+ snprintf(sbp, SPACELEFT(sbuf, sbp), ", msgid=%.100s", mbuf);
+ sbp += strlen(sbp);
+ }
+ if (e->e_bodytype != NULL)
+ {
+ (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", bodytype=%.20s",
+ e->e_bodytype);
+ sbp += strlen(sbp);
+ }
+ p = macvalue('r', e);
+ if (p != NULL)
+ (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", proto=%.20s", p);
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.850s, relay=%.100s",
+ sbuf, name);
+
+# else /* short syslog buffer */
+
+ sm_syslog(LOG_INFO, e->e_id,
+ "from=%s",
+ e->e_from.q_paddr == NULL ? "<NONE>"
+ : shortenstring(e->e_from.q_paddr, 83));
+ sm_syslog(LOG_INFO, e->e_id,
+ "size=%ld, class=%ld, pri=%ld, nrcpts=%d",
+ e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts);
+ if (msgid != NULL)
+ sm_syslog(LOG_INFO, e->e_id,
+ "msgid=%s",
+ shortenstring(mbuf, 83));
+ sbp = sbuf;
+ *sbp = '\0';
+ if (e->e_bodytype != NULL)
+ {
+ snprintf(sbp, SPACELEFT(sbuf, sbp), "bodytype=%.20s, ", e->e_bodytype);
+ sbp += strlen(sbp);
+ }
+ p = macvalue('r', e);
+ if (p != NULL)
+ {
+ snprintf(sbp, SPACELEFT(sbuf, sbp), "proto=%.20s, ", p);
+ sbp += strlen(sbp);
+ }
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.400srelay=%.100s", sbuf, name);
+# endif
+}
+ /*
+** PRIENCODE -- encode external priority names into internal values.
+**
+** Parameters:
+** p -- priority in ascii.
+**
+** Returns:
+** priority as a numeric level.
+**
+** Side Effects:
+** none.
+*/
+
+int
+priencode(p)
+ char *p;
+{
+ register int i;
+
+ for (i = 0; i < NumPriorities; i++)
+ {
+ if (!strcasecmp(p, Priorities[i].pri_name))
+ return (Priorities[i].pri_val);
+ }
+
+ /* unknown priority */
+ return (0);
+}
+ /*
+** CRACKADDR -- parse an address and turn it into a macro
+**
+** This doesn't actually parse the address -- it just extracts
+** it and replaces it with "$g". The parse is totally ad hoc
+** and isn't even guaranteed to leave something syntactically
+** identical to what it started with. However, it does leave
+** something semantically identical.
+**
+** This algorithm has been cleaned up to handle a wider range
+** of cases -- notably quoted and backslash escaped strings.
+** This modification makes it substantially better at preserving
+** the original syntax.
+**
+** Parameters:
+** addr -- the address to be cracked.
+**
+** Returns:
+** a pointer to the new version.
+**
+** Side Effects:
+** none.
+**
+** Warning:
+** The return value is saved in local storage and should
+** be copied if it is to be reused.
+*/
+
+char *
+crackaddr(addr)
+ register char *addr;
+{
+ register char *p;
+ register char c;
+ int cmtlev;
+ int realcmtlev;
+ int anglelev, realanglelev;
+ int copylev;
+ int bracklev;
+ bool qmode;
+ bool realqmode;
+ bool skipping;
+ bool putgmac = FALSE;
+ bool quoteit = FALSE;
+ bool gotangle = FALSE;
+ bool gotcolon = FALSE;
+ register char *bp;
+ char *buflim;
+ char *bufhead;
+ char *addrhead;
+ static char buf[MAXNAME + 1];
+
+ if (tTd(33, 1))
+ printf("crackaddr(%s)\n", addr);
+
+ /* strip leading spaces */
+ while (*addr != '\0' && isascii(*addr) && isspace(*addr))
+ addr++;
+
+ /*
+ ** Start by assuming we have no angle brackets. This will be
+ ** adjusted later if we find them.
+ */
+
+ bp = bufhead = buf;
+ buflim = &buf[sizeof buf - 7];
+ p = addrhead = addr;
+ copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0;
+ bracklev = 0;
+ qmode = realqmode = FALSE;
+
+ while ((c = *p++) != '\0')
+ {
+ /*
+ ** If the buffer is overful, go into a special "skipping"
+ ** mode that tries to keep legal syntax but doesn't actually
+ ** output things.
+ */
+
+ skipping = bp >= buflim;
+
+ if (copylev > 0 && !skipping)
+ *bp++ = c;
+
+ /* check for backslash escapes */
+ if (c == '\\')
+ {
+ /* arrange to quote the address */
+ if (cmtlev <= 0 && !qmode)
+ quoteit = TRUE;
+
+ if ((c = *p++) == '\0')
+ {
+ /* too far */
+ p--;
+ goto putg;
+ }
+ if (copylev > 0 && !skipping)
+ *bp++ = c;
+ goto putg;
+ }
+
+ /* check for quoted strings */
+ if (c == '"' && cmtlev <= 0)
+ {
+ qmode = !qmode;
+ if (copylev > 0 && !skipping)
+ realqmode = !realqmode;
+ continue;
+ }
+ if (qmode)
+ goto putg;
+
+ /* check for comments */
+ if (c == '(')
+ {
+ cmtlev++;
+
+ /* allow space for closing paren */
+ if (!skipping)
+ {
+ buflim--;
+ realcmtlev++;
+ if (copylev++ <= 0)
+ {
+ if (bp != bufhead)
+ *bp++ = ' ';
+ *bp++ = c;
+ }
+ }
+ }
+ if (cmtlev > 0)
+ {
+ if (c == ')')
+ {
+ cmtlev--;
+ copylev--;
+ if (!skipping)
+ {
+ realcmtlev--;
+ buflim++;
+ }
+ }
+ continue;
+ }
+ else if (c == ')')
+ {
+ /* syntax error: unmatched ) */
+ if (copylev > 0 && !skipping)
+ bp--;
+ }
+
+ /* count nesting on [ ... ] (for IPv6 domain literals) */
+ if (c == '[')
+ bracklev++;
+ else if (c == ']')
+ bracklev--;
+
+ /* check for group: list; syntax */
+ if (c == ':' && anglelev <= 0 && bracklev <= 0 &&
+ !gotcolon && !ColonOkInAddr)
+ {
+ register char *q;
+
+ /*
+ ** Check for DECnet phase IV ``::'' (host::user)
+ ** or ** DECnet phase V ``:.'' syntaxes. The latter
+ ** covers ``user@DEC:.tay.myhost'' and
+ ** ``DEC:.tay.myhost::user'' syntaxes (bletch).
+ */
+
+ if (*p == ':' || *p == '.')
+ {
+ if (cmtlev <= 0 && !qmode)
+ quoteit = TRUE;
+ if (copylev > 0 && !skipping)
+ {
+ *bp++ = c;
+ *bp++ = *p;
+ }
+ p++;
+ goto putg;
+ }
+
+ gotcolon = TRUE;
+
+ bp = bufhead;
+ if (quoteit)
+ {
+ *bp++ = '"';
+
+ /* back up over the ':' and any spaces */
+ --p;
+ while (isascii(*--p) && isspace(*p))
+ continue;
+ p++;
+ }
+ for (q = addrhead; q < p; )
+ {
+ c = *q++;
+ if (bp < buflim)
+ {
+ if (quoteit && c == '"')
+ *bp++ = '\\';
+ *bp++ = c;
+ }
+ }
+ if (quoteit)
+ {
+ if (bp == &bufhead[1])
+ bp--;
+ else
+ *bp++ = '"';
+ while ((c = *p++) != ':')
+ {
+ if (bp < buflim)
+ *bp++ = c;
+ }
+ *bp++ = c;
+ }
+
+ /* any trailing white space is part of group: */
+ while (isascii(*p) && isspace(*p) && bp < buflim)
+ *bp++ = *p++;
+ copylev = 0;
+ putgmac = quoteit = FALSE;
+ bufhead = bp;
+ addrhead = p;
+ continue;
+ }
+
+ if (c == ';' && copylev <= 0 && !ColonOkInAddr)
+ {
+ if (bp < buflim)
+ *bp++ = c;
+ }
+
+ /* check for characters that may have to be quoted */
+ if (strchr(MustQuoteChars, c) != NULL)
+ {
+ /*
+ ** If these occur as the phrase part of a <>
+ ** construct, but are not inside of () or already
+ ** quoted, they will have to be quoted. Note that
+ ** now (but don't actually do the quoting).
+ */
+
+ if (cmtlev <= 0 && !qmode)
+ quoteit = TRUE;
+ }
+
+ /* check for angle brackets */
+ if (c == '<')
+ {
+ register char *q;
+
+ /* assume first of two angles is bogus */
+ if (gotangle)
+ quoteit = TRUE;
+ gotangle = TRUE;
+
+ /* oops -- have to change our mind */
+ anglelev = 1;
+ if (!skipping)
+ realanglelev = 1;
+
+ bp = bufhead;
+ if (quoteit)
+ {
+ *bp++ = '"';
+
+ /* back up over the '<' and any spaces */
+ --p;
+ while (isascii(*--p) && isspace(*p))
+ continue;
+ p++;
+ }
+ for (q = addrhead; q < p; )
+ {
+ c = *q++;
+ if (bp < buflim)
+ {
+ if (quoteit && c == '"')
+ *bp++ = '\\';
+ *bp++ = c;
+ }
+ }
+ if (quoteit)
+ {
+ if (bp == &buf[1])
+ bp--;
+ else
+ *bp++ = '"';
+ while ((c = *p++) != '<')
+ {
+ if (bp < buflim)
+ *bp++ = c;
+ }
+ *bp++ = c;
+ }
+ copylev = 0;
+ putgmac = quoteit = FALSE;
+ continue;
+ }
+
+ if (c == '>')
+ {
+ if (anglelev > 0)
+ {
+ anglelev--;
+ if (!skipping)
+ {
+ realanglelev--;
+ buflim++;
+ }
+ }
+ else if (!skipping)
+ {
+ /* syntax error: unmatched > */
+ if (copylev > 0)
+ bp--;
+ quoteit = TRUE;
+ continue;
+ }
+ if (copylev++ <= 0)
+ *bp++ = c;
+ continue;
+ }
+
+ /* must be a real address character */
+ putg:
+ if (copylev <= 0 && !putgmac)
+ {
+ if (bp > bufhead && bp[-1] == ')')
+ *bp++ = ' ';
+ *bp++ = MACROEXPAND;
+ *bp++ = 'g';
+ putgmac = TRUE;
+ }
+ }
+
+ /* repair any syntactic damage */
+ if (realqmode)
+ *bp++ = '"';
+ while (realcmtlev-- > 0)
+ *bp++ = ')';
+ while (realanglelev-- > 0)
+ *bp++ = '>';
+ *bp++ = '\0';
+
+ if (tTd(33, 1))
+ {
+ printf("crackaddr=>`");
+ xputs(buf);
+ printf("'\n");
+ }
+
+ return (buf);
+}
+ /*
+** PUTHEADER -- put the header part of a message from the in-core copy
+**
+** Parameters:
+** mci -- the connection information.
+** h -- the header to put.
+** e -- envelope to use.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+*/
+
+/*
+ * Macro for fast max (not available in e.g. DG/UX, 386/ix).
+ */
+#ifndef MAX
+# define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+void
+putheader(mci, hdr, e)
+ register MCI *mci;
+ HDR *hdr;
+ register ENVELOPE *e;
+{
+ register HDR *h;
+ char buf[MAX(MAXLINE,BUFSIZ)];
+ char obuf[MAXLINE];
+
+ if (tTd(34, 1))
+ printf("--- putheader, mailer = %s ---\n",
+ mci->mci_mailer->m_name);
+
+ /*
+ ** If we're in MIME mode, we're not really in the header of the
+ ** message, just the header of one of the parts of the body of
+ ** the message. Therefore MCIF_INHEADER should not be turned on.
+ */
+
+ if (!bitset(MCIF_INMIME, mci->mci_flags))
+ mci->mci_flags |= MCIF_INHEADER;
+
+ for (h = hdr; h != NULL; h = h->h_link)
+ {
+ register char *p = h->h_value;
+ extern bool bitintersect __P((BITMAP, BITMAP));
+
+ if (tTd(34, 11))
+ {
+ printf(" %s: ", h->h_field);
+ xputs(p);
+ }
+
+ /* suppress Content-Transfer-Encoding: if we are MIMEing */
+ if (bitset(H_CTE, h->h_flags) &&
+ bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags))
+ {
+ if (tTd(34, 11))
+ printf(" (skipped (content-transfer-encoding))\n");
+ continue;
+ }
+
+ if (bitset(MCIF_INMIME, mci->mci_flags))
+ {
+ if (tTd(34, 11))
+ printf("\n");
+ put_vanilla_header(h, p, mci);
+ continue;
+ }
+
+ if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
+ !bitintersect(h->h_mflags, mci->mci_mailer->m_flags))
+ {
+ if (tTd(34, 11))
+ printf(" (skipped)\n");
+ continue;
+ }
+
+ /* handle Resent-... headers specially */
+ if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
+ {
+ if (tTd(34, 11))
+ printf(" (skipped (resent))\n");
+ continue;
+ }
+
+ /* suppress return receipts if requested */
+ if (bitset(H_RECEIPTTO, h->h_flags) &&
+#if _FFR_DSN_RRT_OPTION
+ (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags)))
+#else
+ bitset(EF_NORECEIPT, e->e_flags))
+#endif
+ {
+ if (tTd(34, 11))
+ printf(" (skipped (receipt))\n");
+ continue;
+ }
+
+ /* macro expand value if generated internally */
+ if (bitset(H_DEFAULT, h->h_flags))
+ {
+ expand(p, buf, sizeof buf, e);
+ p = buf;
+ if (*p == '\0')
+ {
+ if (tTd(34, 11))
+ printf(" (skipped -- null value)\n");
+ continue;
+ }
+ }
+
+ if (bitset(H_BCC, h->h_flags))
+ {
+ /* Bcc: field -- either truncate or delete */
+ if (bitset(EF_DELETE_BCC, e->e_flags))
+ {
+ if (tTd(34, 11))
+ printf(" (skipped -- bcc)\n");
+ }
+ else
+ {
+ /* no other recipient headers: truncate value */
+ (void) snprintf(obuf, sizeof obuf, "%s:",
+ h->h_field);
+ putline(obuf, mci);
+ }
+ continue;
+ }
+
+ if (tTd(34, 11))
+ printf("\n");
+
+ if (bitset(H_FROM|H_RCPT, h->h_flags))
+ {
+ /* address field */
+ bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
+
+ if (bitset(H_FROM, h->h_flags))
+ oldstyle = FALSE;
+ commaize(h, p, oldstyle, mci, e);
+ }
+ else
+ {
+ put_vanilla_header(h, p, mci);
+ }
+ }
+
+ /*
+ ** If we are converting this to a MIME message, add the
+ ** MIME headers.
+ */
+
+#if MIME8TO7
+ if (bitset(MM_MIME8BIT, MimeMode) &&
+ bitset(EF_HAS8BIT, e->e_flags) &&
+ !bitset(EF_DONT_MIME, e->e_flags) &&
+ !bitnset(M_8BITS, mci->mci_mailer->m_flags) &&
+ !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8, mci->mci_flags))
+ {
+ if (hvalue("MIME-Version", e->e_header) == NULL)
+ putline("MIME-Version: 1.0", mci);
+ if (hvalue("Content-Type", e->e_header) == NULL)
+ {
+ snprintf(obuf, sizeof obuf,
+ "Content-Type: text/plain; charset=%s",
+ defcharset(e));
+ putline(obuf, mci);
+ }
+ if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL)
+ putline("Content-Transfer-Encoding: 8bit", mci);
+ }
+#endif
+}
+ /*
+** PUT_VANILLA_HEADER -- output a fairly ordinary header
+**
+** Parameters:
+** h -- the structure describing this header
+** v -- the value of this header
+** mci -- the connection info for output
+**
+** Returns:
+** none.
+*/
+
+void
+put_vanilla_header(h, v, mci)
+ HDR *h;
+ char *v;
+ MCI *mci;
+{
+ register char *nlp;
+ register char *obp;
+ int putflags;
+ char obuf[MAXLINE];
+
+ putflags = PXLF_HEADER;
+#if _FFR_7BITHDRS
+ if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
+ putflags |= PXLF_STRIP8BIT;
+#endif
+ (void) snprintf(obuf, sizeof obuf, "%.200s: ", h->h_field);
+ obp = obuf + strlen(obuf);
+ while ((nlp = strchr(v, '\n')) != NULL)
+ {
+ int l;
+
+ l = nlp - v;
+ if (SPACELEFT(obuf, obp) - 1 < l)
+ l = SPACELEFT(obuf, obp) - 1;
+
+ snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v);
+ putxline(obuf, strlen(obuf), mci, putflags);
+ v += l + 1;
+ obp = obuf;
+ if (*v != ' ' && *v != '\t')
+ *obp++ = ' ';
+ }
+ snprintf(obp, SPACELEFT(obuf, obp), "%.*s",
+ sizeof obuf - (obp - obuf) - 1, v);
+ putxline(obuf, strlen(obuf), mci, putflags);
+}
+ /*
+** COMMAIZE -- output a header field, making a comma-translated list.
+**
+** Parameters:
+** h -- the header field to output.
+** p -- the value to put in it.
+** oldstyle -- TRUE if this is an old style header.
+** mci -- the connection information.
+** e -- the envelope containing the message.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** outputs "p" to file "fp".
+*/
+
+void
+commaize(h, p, oldstyle, mci, e)
+ register HDR *h;
+ register char *p;
+ bool oldstyle;
+ register MCI *mci;
+ register ENVELOPE *e;
+{
+ register char *obp;
+ int opos;
+ int omax;
+ bool firstone = TRUE;
+ int putflags = PXLF_HEADER;
+ char obuf[MAXLINE + 3];
+
+ /*
+ ** Output the address list translated by the
+ ** mailer and with commas.
+ */
+
+ if (tTd(14, 2))
+ printf("commaize(%s: %s)\n", h->h_field, p);
+
+#if _FFR_7BITHDRS
+ if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
+ putflags |= PXLF_STRIP8BIT;
+#endif
+
+ obp = obuf;
+ (void) snprintf(obp, SPACELEFT(obuf, obp), "%.200s: ", h->h_field);
+ opos = strlen(h->h_field) + 2;
+ if (opos > 202)
+ opos = 202;
+ obp += opos;
+ omax = mci->mci_mailer->m_linelimit - 2;
+ if (omax < 0 || omax > 78)
+ omax = 78;
+
+ /*
+ ** Run through the list of values.
+ */
+
+ while (*p != '\0')
+ {
+ register char *name;
+ register int c;
+ char savechar;
+ int flags;
+ auto int stat;
+
+ /*
+ ** Find the end of the name. New style names
+ ** end with a comma, old style names end with
+ ** a space character. However, spaces do not
+ ** necessarily delimit an old-style name -- at
+ ** signs mean keep going.
+ */
+
+ /* find end of name */
+ while ((isascii(*p) && isspace(*p)) || *p == ',')
+ p++;
+ name = p;
+ for (;;)
+ {
+ auto char *oldp;
+ char pvpbuf[PSBUFSIZE];
+
+ (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf,
+ sizeof pvpbuf, &oldp, NULL);
+ p = oldp;
+
+ /* look to see if we have an at sign */
+ while (*p != '\0' && isascii(*p) && isspace(*p))
+ p++;
+
+ if (*p != '@')
+ {
+ p = oldp;
+ break;
+ }
+ p += *p == '@' ? 1 : 2;
+ while (*p != '\0' && isascii(*p) && isspace(*p))
+ p++;
+ }
+ /* at the end of one complete name */
+
+ /* strip off trailing white space */
+ while (p >= name &&
+ ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0'))
+ p--;
+ if (++p == name)
+ continue;
+ savechar = *p;
+ *p = '\0';
+
+ /* translate the name to be relative */
+ flags = RF_HEADERADDR|RF_ADDDOMAIN;
+ if (bitset(H_FROM, h->h_flags))
+ flags |= RF_SENDERADDR;
+#if USERDB
+ else if (e->e_from.q_mailer != NULL &&
+ bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags))
+ {
+ extern char *udbsender __P((char *));
+ char *q;
+
+ q = udbsender(name);
+ if (q != NULL)
+ name = q;
+ }
+#endif
+ stat = EX_OK;
+ name = remotename(name, mci->mci_mailer, flags, &stat, e);
+ if (*name == '\0')
+ {
+ *p = savechar;
+ continue;
+ }
+ name = denlstring(name, FALSE, TRUE);
+
+ /* output the name with nice formatting */
+ opos += strlen(name);
+ if (!firstone)
+ opos += 2;
+ if (opos > omax && !firstone)
+ {
+ snprintf(obp, SPACELEFT(obuf, obp), ",\n");
+ putxline(obuf, strlen(obuf), mci, putflags);
+ obp = obuf;
+ (void) strcpy(obp, " ");
+ opos = strlen(obp);
+ obp += opos;
+ opos += strlen(name);
+ }
+ else if (!firstone)
+ {
+ snprintf(obp, SPACELEFT(obuf, obp), ", ");
+ obp += 2;
+ }
+
+ while ((c = *name++) != '\0' && obp < &obuf[MAXLINE])
+ *obp++ = c;
+ firstone = FALSE;
+ *p = savechar;
+ }
+ *obp = '\0';
+ putxline(obuf, strlen(obuf), mci, putflags);
+}
+ /*
+** COPYHEADER -- copy header list
+**
+** This routine is the equivalent of newstr for header lists
+**
+** Parameters:
+** header -- list of header structures to copy.
+**
+** Returns:
+** a copy of 'header'.
+**
+** Side Effects:
+** none.
+*/
+
+HDR *
+copyheader(header)
+ register HDR *header;
+{
+ register HDR *newhdr;
+ HDR *ret;
+ register HDR **tail = &ret;
+
+ while (header != NULL)
+ {
+ newhdr = (HDR *) xalloc(sizeof(HDR));
+ STRUCTCOPY(*header, *newhdr);
+ *tail = newhdr;
+ tail = &newhdr->h_link;
+ header = header->h_link;
+ }
+ *tail = NULL;
+
+ return ret;
+}
diff --git a/contrib/sendmail/src/ldap_map.h b/contrib/sendmail/src/ldap_map.h
new file mode 100644
index 0000000..9f6a679
--- /dev/null
+++ b/contrib/sendmail/src/ldap_map.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. 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.
+ *
+ */
+
+/*
+** Support for LDAP.
+**
+** Contributed by Booker C. Bense <bbense+ldap@stanford.edu>.
+** Please go to him for support -- since I (Eric) don't run LDAP, I
+** can't help you at all.
+**
+** @(#)ldap_map.h 8.9 (Berkeley) 5/19/98
+*/
+
+#ifndef _LDAP_MAP_H
+#define _LDAP_MAP_H
+
+#include <sys/time.h>
+
+struct ldap_map_struct
+{
+ /* needed for ldap_open */
+ char *ldaphost;
+ int ldapport;
+
+ /* Options set in ld struct before ldap_bind_s */
+ int deref;
+ int timelimit;
+ int sizelimit;
+ int ldap_options;
+
+ /* args for ldap_bind_s */
+ LDAP *ld;
+ char *binddn;
+ char *passwd;
+ int method;
+
+ /* args for ldap_search_st */
+ char *base;
+ int scope;
+ char *filter;
+ char *attr[2];
+ int attrsonly;
+ struct timeval timeout;
+ LDAPMessage *res;
+};
+
+typedef struct ldap_map_struct LDAP_MAP_STRUCT;
+
+#define DEFAULT_LDAP_MAP_PORT LDAP_PORT
+#define DEFAULT_LDAP_MAP_SCOPE LDAP_SCOPE_SUBTREE
+#define DEFAULT_LDAP_MAP_BINDDN NULL
+#define DEFAULT_LDAP_MAP_PASSWD NULL
+#define DEFAULT_LDAP_MAP_METHOD LDAP_AUTH_SIMPLE
+#define DEFAULT_LDAP_MAP_TIMELIMIT 5
+#define DEFAULT_LDAP_MAP_DEREF LDAP_DEREF_NEVER
+#define DEFAULT_LDAP_MAP_SIZELIMIT 0
+#define DEFAULT_LDAP_MAP_ATTRSONLY 0
+#define LDAP_MAP_MAX_FILTER 256
+#ifdef LDAP_REFERRALS
+# define DEFAULT_LDAP_MAP_LDAP_OPTIONS LDAP_OPT_REFERRALS
+#else /* LDAP_REFERRALS */
+# define DEFAULT_LDAP_MAP_LDAP_OPTIONS 0
+#endif /* LDAP_REFERRALS */
+
+#endif /* _LDAP_MAP_H */
diff --git a/contrib/sendmail/src/macro.c b/contrib/sendmail/src/macro.c
new file mode 100644
index 0000000..c1f9f7b
--- /dev/null
+++ b/contrib/sendmail/src/macro.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)macro.c 8.25 (Berkeley) 5/19/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+char *MacroName[256]; /* macro id to name table */
+int NextMacroId = 0240; /* codes for long named macros */
+
+
+/*
+** EXPAND -- macro expand a string using $x escapes.
+**
+** Parameters:
+** s -- the string to expand.
+** buf -- the place to put the expansion.
+** bufsize -- the size of the buffer.
+** e -- envelope in which to work.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+*/
+
+void
+expand(s, buf, bufsize, e)
+ register char *s;
+ register char *buf;
+ size_t bufsize;
+ register ENVELOPE *e;
+{
+ register char *xp;
+ register char *q;
+ bool skipping; /* set if conditionally skipping output */
+ bool recurse = FALSE; /* set if recursion required */
+ int i;
+ int skiplev; /* skipping nesting level */
+ int iflev; /* if nesting level */
+ char xbuf[MACBUFSIZE];
+ static int explevel = 0;
+
+ if (tTd(35, 24))
+ {
+ printf("expand(");
+ xputs(s);
+ printf(")\n");
+ }
+
+ skipping = FALSE;
+ skiplev = 0;
+ iflev = 0;
+ if (s == NULL)
+ s = "";
+ for (xp = xbuf; *s != '\0'; s++)
+ {
+ int c;
+
+ /*
+ ** Check for non-ordinary (special?) character.
+ ** 'q' will be the interpolated quantity.
+ */
+
+ q = NULL;
+ c = *s;
+ switch (c & 0377)
+ {
+ case CONDIF: /* see if var set */
+ iflev++;
+ c = *++s;
+ if (skipping)
+ skiplev++;
+ else
+ skipping = macvalue(c, e) == NULL;
+ continue;
+
+ case CONDELSE: /* change state of skipping */
+ if (iflev == 0)
+ break;
+ if (skiplev == 0)
+ skipping = !skipping;
+ continue;
+
+ case CONDFI: /* stop skipping */
+ if (iflev == 0)
+ break;
+ iflev--;
+ if (skiplev == 0)
+ skipping = FALSE;
+ if (skipping)
+ skiplev--;
+ continue;
+
+ case MACROEXPAND: /* macro interpolation */
+ c = *++s & 0377;
+ if (c != '\0')
+ q = macvalue(c, e);
+ else
+ {
+ s--;
+ q = NULL;
+ }
+ if (q == NULL)
+ continue;
+ break;
+ }
+
+ /*
+ ** Interpolate q or output one character
+ */
+
+ if (skipping || xp >= &xbuf[sizeof xbuf - 1])
+ continue;
+ if (q == NULL)
+ *xp++ = c;
+ else
+ {
+ /* copy to end of q or max space remaining in buf */
+ while ((c = *q++) != '\0' && xp < &xbuf[sizeof xbuf - 1])
+ {
+ /* check for any sendmail metacharacters */
+ if ((c & 0340) == 0200)
+ recurse = TRUE;
+ *xp++ = c;
+ }
+ }
+ }
+ *xp = '\0';
+
+ if (tTd(35, 24))
+ {
+ printf("expand ==> ");
+ xputs(xbuf);
+ printf("\n");
+ }
+
+ /* recurse as appropriate */
+ if (recurse)
+ {
+ if (explevel < MaxMacroRecursion)
+ {
+ explevel++;
+ expand(xbuf, buf, bufsize, e);
+ explevel--;
+ return;
+ }
+ syserr("expand: recursion too deep (%d max)",
+ MaxMacroRecursion);
+ }
+
+ /* copy results out */
+ i = xp - xbuf;
+ if (i >= bufsize)
+ i = bufsize - 1;
+ bcopy(xbuf, buf, i);
+ buf[i] = '\0';
+}
+ /*
+** DEFINE -- define a macro.
+**
+** this would be better done using a #define macro.
+**
+** Parameters:
+** n -- the macro name.
+** v -- the macro value.
+** e -- the envelope to store the definition in.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** e->e_macro[n] is defined.
+**
+** Notes:
+** There is one macro for each ASCII character,
+** although they are not all used. The currently
+** defined macros are:
+**
+** $a date in ARPANET format (preferring the Date: line
+** of the message)
+** $b the current date (as opposed to the date as found
+** the message) in ARPANET format
+** $c hop count
+** $d (current) date in UNIX (ctime) format
+** $e the SMTP entry message+
+** $f raw from address
+** $g translated from address
+** $h to host
+** $i queue id
+** $j official SMTP hostname, used in messages+
+** $k UUCP node name
+** $l UNIX-style from line+
+** $m The domain part of our full name.
+** $n name of sendmail ("MAILER-DAEMON" on local
+** net typically)+
+** $o delimiters ("operators") for address tokens+
+** (set via OperatorChars option in V6 or later
+** sendmail.cf files)
+** $p my process id in decimal
+** $q the string that becomes an address -- this is
+** normally used to combine $g & $x.
+** $r protocol used to talk to sender
+** $s sender's host name
+** $t the current time in seconds since 1/1/1970
+** $u to user
+** $v version number of sendmail
+** $w our host name (if it can be determined)
+** $x signature (full name) of from person
+** $y the tty id of our terminal
+** $z home directory of to person
+** $_ RFC1413 authenticated sender address
+**
+** Macros marked with + must be defined in the
+** configuration file and are used internally, but
+** are not set.
+**
+** There are also some macros that can be used
+** arbitrarily to make the configuration file
+** cleaner. In general all upper-case letters
+** are available.
+*/
+
+void
+define(n, v, e)
+ int n;
+ char *v;
+ register ENVELOPE *e;
+{
+ if (tTd(35, 9))
+ {
+ printf("%sdefine(%s as ",
+ (e->e_macro[n & 0377] == NULL) ? "" : "re", macname(n));
+ xputs(v);
+ printf(")\n");
+ }
+ e->e_macro[n & 0377] = v;
+}
+ /*
+** MACVALUE -- return uninterpreted value of a macro.
+**
+** Parameters:
+** n -- the name of the macro.
+**
+** Returns:
+** The value of n.
+**
+** Side Effects:
+** none.
+*/
+
+char *
+macvalue(n, e)
+ int n;
+ register ENVELOPE *e;
+{
+ n &= 0377;
+ while (e != NULL)
+ {
+ register char *p = e->e_macro[n];
+
+ if (p != NULL)
+ return (p);
+ e = e->e_parent;
+ }
+ return (NULL);
+}
+ /*
+** MACNAME -- return the name of a macro given its internal id
+**
+** Parameter:
+** n -- the id of the macro
+**
+** Returns:
+** The name of n.
+**
+** Side Effects:
+** none.
+*/
+
+char *
+macname(n)
+ int n;
+{
+ static char mbuf[2];
+
+ n &= 0377;
+ if (bitset(0200, n))
+ {
+ char *p = MacroName[n];
+
+ if (p != NULL)
+ return p;
+ return "***UNDEFINED MACRO***";
+ }
+ mbuf[0] = n;
+ mbuf[1] = '\0';
+ return mbuf;
+}
+ /*
+** MACID -- return id of macro identified by its name
+**
+** Parameters:
+** p -- pointer to name string -- either a single
+** character or {name}.
+** ep -- filled in with the pointer to the byte
+** after the name.
+**
+** Returns:
+** The internal id code for this macro. This will
+** fit into a single byte.
+**
+** Side Effects:
+** If this is a new macro name, a new id is allocated.
+*/
+
+int
+macid(p, ep)
+ register char *p;
+ char **ep;
+{
+ int mid;
+ register char *bp;
+ char mbuf[21];
+
+ if (tTd(35, 14))
+ {
+ printf("macid(");
+ xputs(p);
+ printf(") => ");
+ }
+
+ if (*p == '\0' || (p[0] == '{' && p[1] == '}'))
+ {
+ syserr("Name required for macro/class");
+ if (ep != NULL)
+ *ep = p;
+ if (tTd(35, 14))
+ printf("NULL\n");
+ return '\0';
+ }
+ if (*p != '{')
+ {
+ /* the macro is its own code */
+ if (ep != NULL)
+ *ep = p + 1;
+ if (tTd(35, 14))
+ printf("%c\n", *p);
+ return *p;
+ }
+ bp = mbuf;
+ while (*++p != '\0' && *p != '}' && bp < &mbuf[sizeof mbuf])
+ {
+ if (isascii(*p) && (isalnum(*p) || *p == '_'))
+ *bp++ = *p;
+ else
+ syserr("Invalid macro/class character %c", *p);
+ }
+ *bp = '\0';
+ mid = -1;
+ if (*p == '\0')
+ {
+ syserr("Unbalanced { on %s", mbuf); /* missing } */
+ }
+ else if (*p != '}')
+ {
+ syserr("Macro/class name ({%s}) too long (%d chars max)",
+ mbuf, sizeof mbuf - 1);
+ }
+ else if (mbuf[1] == '\0')
+ {
+ /* ${x} == $x */
+ mid = mbuf[0];
+ p++;
+ }
+ else
+ {
+ register STAB *s;
+
+ s = stab(mbuf, ST_MACRO, ST_ENTER);
+ if (s->s_macro != 0)
+ mid = s->s_macro;
+ else
+ {
+ if (NextMacroId > 0377)
+ {
+ syserr("Macro/class {%s}: too many long names", mbuf);
+ s->s_macro = -1;
+ }
+ else
+ {
+ MacroName[NextMacroId] = s->s_name;
+ s->s_macro = mid = NextMacroId++;
+ }
+ }
+ p++;
+ }
+ if (ep != NULL)
+ *ep = p;
+ if (tTd(35, 14))
+ printf("0x%x\n", mid);
+ return mid;
+}
+ /*
+** WORDINCLASS -- tell if a word is in a specific class
+**
+** Parameters:
+** str -- the name of the word to look up.
+** cl -- the class name.
+**
+** Returns:
+** TRUE if str can be found in cl.
+** FALSE otherwise.
+*/
+
+bool
+wordinclass(str, cl)
+ char *str;
+ int cl;
+{
+ register STAB *s;
+
+ s = stab(str, ST_CLASS, ST_FIND);
+ return s != NULL && bitnset(cl & 0xff, s->s_class);
+}
diff --git a/contrib/sendmail/src/mailq.1 b/contrib/sendmail/src/mailq.1
new file mode 100644
index 0000000..fa1d0d5
--- /dev/null
+++ b/contrib/sendmail/src/mailq.1
@@ -0,0 +1,67 @@
+.\" Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+.\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved.
+.\" Copyright (c) 1985, 1990, 1993
+.\" The Regents of the University of California. 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.
+.\"
+.\"
+.\" @(#)mailq.1 8.10 (Berkeley) 5/19/98
+.\"
+.Dd May 19, 1998
+.Dt MAILQ 1
+.Os BSD 4
+.Sh NAME
+.Nm mailq
+.Nd print the mail queue
+.Sh SYNOPSIS
+.Nm mailq
+.Op Fl v
+.Sh DESCRIPTION
+.Nm Mailq
+prints a summary of the mail messages queued for future delivery.
+.Pp
+The first line printed for each message
+shows the internal identifier used on this host
+for the message,
+the size of the message in bytes,
+the date and time the message was accepted into the queue,
+and the envelope sender of the message.
+The second line shows the error message that caused this message
+to be retained in the queue;
+it will not be present if the message is being processed
+for the first time.
+The following lines show message recipients,
+one per line.
+.Pp
+.Nm Mailq
+is identical to
+.Dq Li "sendmail -bp" .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl v
+Print verbose information.
+This adds the priority of the message and
+a single character indicator (``+'' or blank)
+indicating whether a warning message has been sent
+on the first line of the message.
+Additionally, extra lines may be intermixed with the recipients
+indicating the ``controlling user'' information;
+this shows who will own any programs that are executed
+on behalf of this message
+and the name of the alias this command expanded from, if any.
+.El
+.Pp
+The
+.Nm mailq
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr sendmail 8
+.Sh HISTORY
+The
+.Nm mailq
+command appeared in
+.Bx 4.0 .
diff --git a/contrib/sendmail/src/mailstats.h b/contrib/sendmail/src/mailstats.h
new file mode 100644
index 0000000..2121e14
--- /dev/null
+++ b/contrib/sendmail/src/mailstats.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ *
+ * @(#)mailstats.h 8.8 (Berkeley) 5/19/98
+ */
+
+#define STAT_VERSION 2
+#define STAT_MAGIC 0x1B1DE
+
+/*
+** Statistics structure.
+*/
+
+struct statistics
+{
+ int stat_magic; /* magic number */
+ int stat_version; /* stat file version */
+ time_t stat_itime; /* file initialization time */
+ short stat_size; /* size of this structure */
+ long stat_nf[MAXMAILERS]; /* # msgs from each mailer */
+ long stat_bf[MAXMAILERS]; /* kbytes from each mailer */
+ long stat_nt[MAXMAILERS]; /* # msgs to each mailer */
+ long stat_bt[MAXMAILERS]; /* kbytes to each mailer */
+ long stat_nr[MAXMAILERS]; /* # rejects by each mailer */
+ long stat_nd[MAXMAILERS]; /* # discards by each mailer */
+};
diff --git a/contrib/sendmail/src/main.c b/contrib/sendmail/src/main.c
new file mode 100644
index 0000000..f014d83
--- /dev/null
+++ b/contrib/sendmail/src/main.c
@@ -0,0 +1,2701 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1998 Sendmail, Inc. All rights reserved.\n\
+ Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\
+ Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.302 (Berkeley) 6/4/98";
+#endif /* not lint */
+
+#define _DEFINE
+
+#include "sendmail.h"
+#include <arpa/inet.h>
+#include <grp.h>
+#if NAMED_BIND
+#include <resolv.h>
+#endif
+
+/*
+** SENDMAIL -- Post mail to a set of destinations.
+**
+** This is the basic mail router. All user mail programs should
+** call this routine to actually deliver mail. Sendmail in
+** turn calls a bunch of mail servers that do the real work of
+** delivering the mail.
+**
+** Sendmail is driven by settings read in from /etc/sendmail.cf
+** (read by readcf.c).
+**
+** Usage:
+** /usr/lib/sendmail [flags] addr ...
+**
+** See the associated documentation for details.
+**
+** Author:
+** Eric Allman, UCB/INGRES (until 10/81).
+** Britton-Lee, Inc., purveyors of fine
+** database computers (11/81 - 10/88).
+** International Computer Science Institute
+** (11/88 - 9/89).
+** UCB/Mammoth Project (10/89 - 7/95).
+** InReference, Inc. (8/95 - 1/97).
+** Sendmail, Inc. (1/98 - present).
+** The support of the my employers is gratefully acknowledged.
+** Few of them (Britton-Lee in particular) have had
+** anything to gain from my involvement in this project.
+*/
+
+
+int NextMailer; /* "free" index into Mailer struct */
+char *FullName; /* sender's full name */
+ENVELOPE BlankEnvelope; /* a "blank" envelope */
+ENVELOPE MainEnvelope; /* the envelope around the basic letter */
+ADDRESS NullAddress = /* a null address */
+ { "", "", NULL, "" };
+char *CommandLineArgs; /* command line args for pid file */
+bool Warn_Q_option = FALSE; /* warn about Q option use */
+char **SaveArgv; /* argument vector for re-execing */
+int MissingFds = 0; /* bit map of fds missing on startup */
+
+#ifdef NGROUPS_MAX
+GIDSET_T InitialGidSet[NGROUPS_MAX];
+#endif
+
+static void obsolete __P((char **));
+extern void printmailer __P((MAILER *));
+extern void tTflag __P((char *));
+
+#if DAEMON && !SMTP
+ERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR
+#endif /* DAEMON && !SMTP */
+#if SMTP && !QUEUE
+ERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR
+#endif /* DAEMON && !SMTP */
+
+#define MAXCONFIGLEVEL 8 /* highest config version level known */
+
+int
+main(argc, argv, envp)
+ int argc;
+ char **argv;
+ char **envp;
+{
+ register char *p;
+ char **av;
+ extern char Version[];
+ char *ep, *from;
+ STAB *st;
+ register int i;
+ int j;
+ bool queuemode = FALSE; /* process queue requests */
+ bool safecf = TRUE;
+ bool warn_C_flag = FALSE;
+ char warn_f_flag = '\0';
+ bool run_in_foreground = FALSE; /* -bD mode */
+ static bool reenter = FALSE;
+ struct passwd *pw;
+ struct hostent *hp;
+ char *nullserver = NULL;
+ bool forged;
+ char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
+ static char rnamebuf[MAXNAME]; /* holds RealUserName */
+ char *emptyenviron[1];
+ QUEUE_CHAR *new;
+ extern int DtableSize;
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+ extern char **environ;
+ extern time_t convtime __P((char *, char));
+ extern SIGFUNC_DECL intsig __P((int));
+ extern struct hostent *myhostname __P((char *, int));
+ extern char *getauthinfo __P((int, bool *));
+ extern char *getcfname __P((void));
+ extern SIGFUNC_DECL sigusr1 __P((int));
+ extern SIGFUNC_DECL sighup __P((int));
+ extern void initmacros __P((ENVELOPE *));
+ extern void init_md __P((int, char **));
+ extern int getdtsize __P((void));
+ extern void tTsetup __P((u_char *, int, char *));
+ extern void setdefaults __P((ENVELOPE *));
+ extern void initsetproctitle __P((int, char **, char **));
+ extern void init_vendor_macros __P((ENVELOPE *));
+ extern void load_if_names __P((void));
+ extern void vendor_pre_defaults __P((ENVELOPE *));
+ extern void vendor_post_defaults __P((ENVELOPE *));
+ extern void readcf __P((char *, bool, ENVELOPE *));
+ extern void printqueue __P((void));
+ extern void sendtoargv __P((char **, ENVELOPE *));
+ extern void resetlimits __P((void));
+#ifndef HASUNSETENV
+ extern void unsetenv __P((char *));
+#endif
+
+ /*
+ ** Check to see if we reentered.
+ ** This would normally happen if e_putheader or e_putbody
+ ** were NULL when invoked.
+ */
+
+ if (reenter)
+ {
+ syserr("main: reentered!");
+ abort();
+ }
+ reenter = TRUE;
+
+ /* avoid null pointer dereferences */
+ TermEscape.te_rv_on = TermEscape.te_rv_off = "";
+
+ /* do machine-dependent initializations */
+ init_md(argc, argv);
+
+ /* in 4.4BSD, the table can be huge; impose a reasonable limit */
+ DtableSize = getdtsize();
+ if (DtableSize > 256)
+ DtableSize = 256;
+
+ /*
+ ** Be sure we have enough file descriptors.
+ ** But also be sure that 0, 1, & 2 are open.
+ */
+
+ fill_fd(STDIN_FILENO, NULL);
+ fill_fd(STDOUT_FILENO, NULL);
+ fill_fd(STDERR_FILENO, NULL);
+
+ i = DtableSize;
+ while (--i > 0)
+ {
+ if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
+ (void) close(i);
+ }
+ errno = 0;
+
+#if LOG
+# ifdef LOG_MAIL
+ openlog("sendmail", LOG_PID, LOG_MAIL);
+# else
+ openlog("sendmail", LOG_PID);
+# endif
+#endif
+
+ if (MissingFds != 0)
+ {
+ char mbuf[MAXLINE];
+
+ mbuf[0] = '\0';
+ if (bitset(1 << STDIN_FILENO, MissingFds))
+ strcat(mbuf, ", stdin");
+ if (bitset(1 << STDOUT_FILENO, MissingFds))
+ strcat(mbuf, ", stdout");
+ if (bitset(1 << STDERR_FILENO, MissingFds))
+ strcat(mbuf, ", stderr");
+ syserr("File descriptors missing on startup: %s", &mbuf[2]);
+ }
+
+ /* reset status from syserr() calls for missing file descriptors */
+ Errors = 0;
+ ExitStat = EX_OK;
+
+#if XDEBUG
+ checkfd012("after openlog");
+#endif
+
+ tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
+
+#ifdef NGROUPS_MAX
+ /* save initial group set for future checks */
+ i = getgroups(NGROUPS_MAX, InitialGidSet);
+ if (i == 0)
+ InitialGidSet[0] = (GID_T) -1;
+ while (i < NGROUPS_MAX)
+ InitialGidSet[i++] = InitialGidSet[0];
+#endif
+
+ /* drop group id privileges (RunAsUser not yet set) */
+ (void) drop_privileges(FALSE);
+
+#ifdef SIGUSR1
+ /* arrange to dump state on user-1 signal */
+ setsignal(SIGUSR1, sigusr1);
+#endif
+
+ /* initialize for setproctitle */
+ initsetproctitle(argc, argv, envp);
+
+ /* Handle any non-getoptable constructions. */
+ obsolete(argv);
+
+ /*
+ ** Do a quick prescan of the argument list.
+ */
+
+#if defined(__osf__) || defined(_AIX3)
+# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x"
+#endif
+#if defined(sony_news)
+# define OPTIONS "B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
+#endif
+#ifndef OPTIONS
+# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:"
+#endif
+ opterr = 0;
+ while ((j = getopt(argc, argv, OPTIONS)) != -1)
+ {
+ switch (j)
+ {
+ case 'd':
+ /* hack attack -- see if should use ANSI mode */
+ if (strcmp(optarg, "ANSI") == 0)
+ {
+ TermEscape.te_rv_on = "\033[7m";
+ TermEscape.te_rv_off = "\033[0m";
+ break;
+ }
+ tTflag(optarg);
+ setbuf(stdout, (char *) NULL);
+ break;
+ }
+ }
+ opterr = 1;
+
+ /* set up the blank envelope */
+ BlankEnvelope.e_puthdr = putheader;
+ BlankEnvelope.e_putbody = putbody;
+ BlankEnvelope.e_xfp = NULL;
+ STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
+ CurEnv = &BlankEnvelope;
+ STRUCTCOPY(NullAddress, MainEnvelope.e_from);
+
+ /*
+ ** Set default values for variables.
+ ** These cannot be in initialized data space.
+ */
+
+ setdefaults(&BlankEnvelope);
+
+ RealUid = getuid();
+ RealGid = getgid();
+
+ pw = sm_getpwuid(RealUid);
+ if (pw != NULL)
+ (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
+ else
+ (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid);
+ RealUserName = rnamebuf;
+
+ if (tTd(0, 101))
+ {
+ printf("Version %s\n", Version);
+ endpwent();
+ setuid(RealUid);
+ exit(EX_OK);
+ }
+
+ /*
+ ** if running non-setuid binary as non-root, pretend
+ ** we are the RunAsUid
+ */
+ if (RealUid != 0 && geteuid() == RealUid)
+ {
+ if (tTd(47, 1))
+ printf("Non-setuid binary: RunAsUid = RealUid = %d\n",
+ (int)RealUid);
+ RunAsUid = RealUid;
+ }
+ else if (geteuid() != 0)
+ RunAsUid = geteuid();
+
+ if (RealUid != 0 && getegid() == RealGid)
+ RunAsGid = RealGid;
+
+ if (tTd(47, 5))
+ {
+ printf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
+ (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
+ printf("main: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
+ }
+
+ /* save command line arguments */
+ i = 0;
+ for (av = argv; *av != NULL; )
+ i += strlen(*av++) + 1;
+ SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
+ CommandLineArgs = xalloc(i);
+ p = CommandLineArgs;
+ for (av = argv, i = 0; *av != NULL; )
+ {
+ SaveArgv[i++] = newstr(*av);
+ if (av != argv)
+ *p++ = ' ';
+ strcpy(p, *av++);
+ p += strlen(p);
+ }
+ SaveArgv[i] = NULL;
+
+ if (tTd(0, 1))
+ {
+ int ll;
+ extern char *CompileOptions[];
+
+ printf("Version %s\n Compiled with:", Version);
+ av = CompileOptions;
+ ll = 7;
+ while (*av != NULL)
+ {
+ if (ll + strlen(*av) > 63)
+ {
+ putchar('\n');
+ ll = 0;
+ }
+ if (ll == 0)
+ {
+ putchar('\t');
+ putchar('\t');
+ }
+ else
+ putchar(' ');
+ printf("%s", *av);
+ ll += strlen(*av++) + 1;
+ }
+ putchar('\n');
+ }
+ if (tTd(0, 10))
+ {
+ int ll;
+ extern char *OsCompileOptions[];
+
+ printf(" OS Defines:");
+ av = OsCompileOptions;
+ ll = 7;
+ while (*av != NULL)
+ {
+ if (ll + strlen(*av) > 63)
+ {
+ putchar('\n');
+ ll = 0;
+ }
+ if (ll == 0)
+ {
+ putchar('\t');
+ putchar('\t');
+ }
+ else
+ putchar(' ');
+ printf("%s", *av);
+ ll += strlen(*av++) + 1;
+ }
+ putchar('\n');
+#ifdef _PATH_UNIX
+ printf("Kernel symbols:\t%s\n", _PATH_UNIX);
+#endif
+ printf(" Def Conf file:\t%s\n", getcfname());
+ printf(" Pid file:\t%s\n", PidFile);
+ }
+
+ InChannel = stdin;
+ OutChannel = stdout;
+
+ /* clear sendmail's environment */
+ ExternalEnviron = environ;
+ emptyenviron[0] = NULL;
+ environ = emptyenviron;
+
+ /*
+ ** restore any original TZ setting until TimeZoneSpec has been
+ ** determined - or early log messages may get bogus time stamps
+ */
+ if ((p = getextenv("TZ")) != NULL)
+ {
+ char *tz;
+ int tzlen;
+
+ tzlen = strlen(p) + 4;
+ tz = xalloc(tzlen);
+ snprintf(tz, tzlen, "TZ=%s", p);
+ putenv(tz);
+ }
+
+ /* prime the child environment */
+ setuserenv("AGENT", "sendmail");
+
+ if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void) setsignal(SIGINT, intsig);
+ (void) setsignal(SIGTERM, intsig);
+ (void) setsignal(SIGPIPE, SIG_IGN);
+ OldUmask = umask(022);
+ OpMode = MD_DELIVER;
+ FullName = getextenv("NAME");
+
+ /*
+ ** Initialize name server if it is going to be used.
+ */
+
+#if NAMED_BIND
+ if (!bitset(RES_INIT, _res.options))
+ res_init();
+ if (tTd(8, 8))
+ _res.options |= RES_DEBUG;
+ else
+ _res.options &= ~RES_DEBUG;
+# ifdef RES_NOALIASES
+ _res.options |= RES_NOALIASES;
+# endif
+#endif
+
+ errno = 0;
+ from = NULL;
+
+ /* initialize some macros, etc. */
+ initmacros(CurEnv);
+ init_vendor_macros(CurEnv);
+
+ /* version */
+ define('v', Version, CurEnv);
+
+ /* hostname */
+ hp = myhostname(jbuf, sizeof jbuf);
+ if (jbuf[0] != '\0')
+ {
+ struct utsname utsname;
+
+ if (tTd(0, 4))
+ printf("canonical name: %s\n", jbuf);
+ define('w', newstr(jbuf), CurEnv); /* must be new string */
+ define('j', newstr(jbuf), CurEnv);
+ setclass('w', jbuf);
+
+ p = strchr(jbuf, '.');
+ if (p != NULL)
+ {
+ if (p[1] != '\0')
+ {
+ define('m', newstr(&p[1]), CurEnv);
+ }
+ while (p != NULL && strchr(&p[1], '.') != NULL)
+ {
+ *p = '\0';
+ if (tTd(0, 4))
+ printf("\ta.k.a.: %s\n", jbuf);
+ setclass('w', jbuf);
+ *p++ = '.';
+ p = strchr(p, '.');
+ }
+ }
+
+ if (uname(&utsname) >= 0)
+ p = utsname.nodename;
+ else
+ {
+ if (tTd(0, 22))
+ printf("uname failed (%s)\n", errstring(errno));
+ makelower(jbuf);
+ p = jbuf;
+ }
+ if (tTd(0, 4))
+ printf(" UUCP nodename: %s\n", p);
+ p = newstr(p);
+ define('k', p, CurEnv);
+ setclass('k', p);
+ setclass('w', p);
+ }
+ if (hp != NULL)
+ {
+ for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
+ {
+ if (tTd(0, 4))
+ printf("\ta.k.a.: %s\n", *av);
+ setclass('w', *av);
+ }
+#if NETINET
+ if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ)
+ {
+ for (i = 0; hp->h_addr_list[i] != NULL; i++)
+ {
+ char ipbuf[103];
+
+ snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
+ inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
+ if (tTd(0, 4))
+ printf("\ta.k.a.: %s\n", ipbuf);
+ setclass('w', ipbuf);
+ }
+ }
+#endif
+ }
+
+ /* current time */
+ define('b', arpadate((char *) NULL), CurEnv);
+
+ QueueLimitRecipient = (QUEUE_CHAR *) NULL;
+ QueueLimitSender = (QUEUE_CHAR *) NULL;
+ QueueLimitId = (QUEUE_CHAR *) NULL;
+
+ /*
+ ** Crack argv.
+ */
+
+ av = argv;
+ p = strrchr(*av, '/');
+ if (p++ == NULL)
+ p = *av;
+ if (strcmp(p, "newaliases") == 0)
+ OpMode = MD_INITALIAS;
+ else if (strcmp(p, "mailq") == 0)
+ OpMode = MD_PRINT;
+ else if (strcmp(p, "smtpd") == 0)
+ OpMode = MD_DAEMON;
+ else if (strcmp(p, "hoststat") == 0)
+ OpMode = MD_HOSTSTAT;
+ else if (strcmp(p, "purgestat") == 0)
+ OpMode = MD_PURGESTAT;
+
+ optind = 1;
+ while ((j = getopt(argc, argv, OPTIONS)) != -1)
+ {
+ switch (j)
+ {
+ case 'b': /* operations mode */
+ switch (j = *optarg)
+ {
+ case MD_DAEMON:
+ case MD_FGDAEMON:
+# if !DAEMON
+ usrerr("Daemon mode not implemented");
+ ExitStat = EX_USAGE;
+ break;
+# endif /* DAEMON */
+ case MD_SMTP:
+# if !SMTP
+ usrerr("I don't speak SMTP");
+ ExitStat = EX_USAGE;
+ break;
+# endif /* SMTP */
+
+ case MD_INITALIAS:
+ case MD_DELIVER:
+ case MD_VERIFY:
+ case MD_TEST:
+ case MD_PRINT:
+ case MD_HOSTSTAT:
+ case MD_PURGESTAT:
+ case MD_ARPAFTP:
+ OpMode = j;
+ break;
+
+ case MD_FREEZE:
+ usrerr("Frozen configurations unsupported");
+ ExitStat = EX_USAGE;
+ break;
+
+ default:
+ usrerr("Invalid operation mode %c", j);
+ ExitStat = EX_USAGE;
+ break;
+ }
+ break;
+
+ case 'B': /* body type */
+ CurEnv->e_bodytype = optarg;
+ break;
+
+ case 'C': /* select configuration file (already done) */
+ if (RealUid != 0)
+ warn_C_flag = TRUE;
+ ConfFile = optarg;
+ (void) drop_privileges(TRUE);
+ safecf = FALSE;
+ break;
+
+ case 'd': /* debugging -- already done */
+ break;
+
+ case 'f': /* from address */
+ case 'r': /* obsolete -f flag */
+ if (from != NULL)
+ {
+ usrerr("More than one \"from\" person");
+ ExitStat = EX_USAGE;
+ break;
+ }
+ from = newstr(denlstring(optarg, TRUE, TRUE));
+ if (strcmp(RealUserName, from) != 0)
+ warn_f_flag = j;
+ break;
+
+ case 'F': /* set full name */
+ FullName = newstr(optarg);
+ break;
+
+ case 'h': /* hop count */
+ CurEnv->e_hopcount = strtol(optarg, &ep, 10);
+ if (*ep)
+ {
+ usrerr("Bad hop count (%s)", optarg);
+ ExitStat = EX_USAGE;
+ }
+ break;
+
+ case 'n': /* don't alias */
+ NoAlias = TRUE;
+ break;
+
+ case 'N': /* delivery status notifications */
+ DefaultNotify |= QHASNOTIFY;
+ if (strcasecmp(optarg, "never") == 0)
+ break;
+ for (p = optarg; p != NULL; optarg = p)
+ {
+ p = strchr(p, ',');
+ if (p != NULL)
+ *p++ = '\0';
+ if (strcasecmp(optarg, "success") == 0)
+ DefaultNotify |= QPINGONSUCCESS;
+ else if (strcasecmp(optarg, "failure") == 0)
+ DefaultNotify |= QPINGONFAILURE;
+ else if (strcasecmp(optarg, "delay") == 0)
+ DefaultNotify |= QPINGONDELAY;
+ else
+ {
+ usrerr("Invalid -N argument");
+ ExitStat = EX_USAGE;
+ }
+ }
+ break;
+
+ case 'o': /* set option */
+ setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
+ break;
+
+ case 'O': /* set option (long form) */
+ setoption(' ', optarg, FALSE, TRUE, CurEnv);
+ break;
+
+ case 'p': /* set protocol */
+ p = strchr(optarg, ':');
+ if (p != NULL)
+ {
+ *p++ = '\0';
+ if (*p != '\0')
+ {
+ ep = xalloc(strlen(p) + 1);
+ cleanstrcpy(ep, p, MAXNAME);
+ define('s', ep, CurEnv);
+ }
+ }
+ if (*optarg != '\0')
+ {
+ ep = xalloc(strlen(optarg) + 1);
+ cleanstrcpy(ep, optarg, MAXNAME);
+ define('r', ep, CurEnv);
+ }
+ break;
+
+ case 'q': /* run queue files at intervals */
+# if QUEUE
+ FullName = NULL;
+ queuemode = TRUE;
+ switch (optarg[0])
+ {
+ case 'I':
+ if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
+ syserr("!Out of memory!!");
+ new->queue_match = newstr(&optarg[1]);
+ new->queue_next = QueueLimitId;
+ QueueLimitId = new;
+ break;
+
+ case 'R':
+ if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
+ syserr("!Out of memory!!");
+ new->queue_match = newstr(&optarg[1]);
+ new->queue_next = QueueLimitRecipient;
+ QueueLimitRecipient = new;
+ break;
+
+ case 'S':
+ if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
+ syserr("!Out of memory!!");
+ new->queue_match = newstr(&optarg[1]);
+ new->queue_next = QueueLimitSender;
+ QueueLimitSender = new;
+ break;
+
+ default:
+ QueueIntvl = convtime(optarg, 'm');
+ break;
+ }
+# else /* QUEUE */
+ usrerr("I don't know about queues");
+ ExitStat = EX_USAGE;
+# endif /* QUEUE */
+ break;
+
+ case 'R': /* DSN RET: what to return */
+ if (bitset(EF_RET_PARAM, CurEnv->e_flags))
+ {
+ usrerr("Duplicate -R flag");
+ ExitStat = EX_USAGE;
+ break;
+ }
+ CurEnv->e_flags |= EF_RET_PARAM;
+ if (strcasecmp(optarg, "hdrs") == 0)
+ CurEnv->e_flags |= EF_NO_BODY_RETN;
+ else if (strcasecmp(optarg, "full") != 0)
+ {
+ usrerr("Invalid -R value");
+ ExitStat = EX_USAGE;
+ }
+ break;
+
+ case 't': /* read recipients from message */
+ GrabTo = TRUE;
+ break;
+
+ case 'U': /* initial (user) submission */
+ UserSubmission = TRUE;
+ break;
+
+ case 'V': /* DSN ENVID: set "original" envelope id */
+ if (!xtextok(optarg))
+ {
+ usrerr("Invalid syntax in -V flag");
+ ExitStat = EX_USAGE;
+ }
+ else
+ CurEnv->e_envid = newstr(optarg);
+ break;
+
+ case 'X': /* traffic log file */
+ (void) drop_privileges(TRUE);
+ TrafficLogFile = fopen(optarg, "a");
+ if (TrafficLogFile == NULL)
+ {
+ syserr("cannot open %s", optarg);
+ ExitStat = EX_CANTCREAT;
+ break;
+ }
+#ifdef HASSETVBUF
+ setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
+#else
+ setlinebuf(TrafficLogFile);
+#endif
+ break;
+
+ /* compatibility flags */
+ case 'c': /* connect to non-local mailers */
+ case 'i': /* don't let dot stop me */
+ case 'm': /* send to me too */
+ case 'T': /* set timeout interval */
+ case 'v': /* give blow-by-blow description */
+ setoption(j, "T", FALSE, TRUE, CurEnv);
+ break;
+
+ case 'e': /* error message disposition */
+ case 'M': /* define macro */
+ setoption(j, optarg, FALSE, TRUE, CurEnv);
+ break;
+
+ case 's': /* save From lines in headers */
+ setoption('f', "T", FALSE, TRUE, CurEnv);
+ break;
+
+# ifdef DBM
+ case 'I': /* initialize alias DBM file */
+ OpMode = MD_INITALIAS;
+ break;
+# endif /* DBM */
+
+# if defined(__osf__) || defined(_AIX3)
+ case 'x': /* random flag that OSF/1 & AIX mailx passes */
+ break;
+# endif
+# if defined(sony_news)
+ case 'E':
+ case 'J': /* ignore flags for Japanese code conversion
+ impremented on Sony NEWS */
+ break;
+# endif
+
+ default:
+ ExitStat = EX_USAGE;
+ finis();
+ break;
+ }
+ }
+ av += optind;
+
+ /*
+ ** Do basic initialization.
+ ** Read system control file.
+ ** Extract special fields for local use.
+ */
+
+ /* set up ${opMode} for use in config file */
+ {
+ char mbuf[2];
+
+ mbuf[0] = OpMode;
+ mbuf[1] = '\0';
+ define(MID_OPMODE, newstr(mbuf), CurEnv);
+ }
+
+#if XDEBUG
+ checkfd012("before readcf");
+#endif
+ vendor_pre_defaults(CurEnv);
+ readcf(getcfname(), safecf, CurEnv);
+ ConfigFileRead = TRUE;
+ vendor_post_defaults(CurEnv);
+
+ /* Enforce use of local time (null string overrides this) */
+ if (TimeZoneSpec == NULL)
+ unsetenv("TZ");
+ else if (TimeZoneSpec[0] != '\0')
+ setuserenv("TZ", TimeZoneSpec);
+ else
+ setuserenv("TZ", NULL);
+ tzset();
+
+ /* avoid denial-of-service attacks */
+ resetlimits();
+
+ if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
+ {
+ /* drop privileges -- daemon mode done after socket/bind */
+ (void) drop_privileges(FALSE);
+ }
+
+ /*
+ ** Find our real host name for future logging.
+ */
+
+ p = getauthinfo(STDIN_FILENO, &forged);
+ define('_', p, CurEnv);
+
+ /* suppress error printing if errors mailed back or whatever */
+ if (CurEnv->e_errormode != EM_PRINT)
+ HoldErrs = TRUE;
+
+ /* set up the $=m class now, after .cf has a chance to redefine $m */
+ expand("\201m", jbuf, sizeof jbuf, CurEnv);
+ setclass('m', jbuf);
+
+ /* probe interfaces and locate any additional names */
+ if (!DontProbeInterfaces)
+ load_if_names();
+
+ if (tTd(0, 1))
+ {
+ printf("\n============ SYSTEM IDENTITY (after readcf) ============");
+ printf("\n (short domain name) $w = ");
+ xputs(macvalue('w', CurEnv));
+ printf("\n (canonical domain name) $j = ");
+ xputs(macvalue('j', CurEnv));
+ printf("\n (subdomain name) $m = ");
+ xputs(macvalue('m', CurEnv));
+ printf("\n (node name) $k = ");
+ xputs(macvalue('k', CurEnv));
+ printf("\n========================================================\n\n");
+ }
+
+ /*
+ ** Do more command line checking -- these are things that
+ ** have to modify the results of reading the config file.
+ */
+
+ /* process authorization warnings from command line */
+ if (warn_C_flag)
+ auth_warning(CurEnv, "Processed by %s with -C %s",
+ RealUserName, ConfFile);
+ if (Warn_Q_option)
+ auth_warning(CurEnv, "Processed from queue %s", QueueDir);
+
+ /* check body type for legality */
+ if (CurEnv->e_bodytype == NULL)
+ /* nothing */ ;
+ else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
+ SevenBitInput = TRUE;
+ else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
+ SevenBitInput = FALSE;
+ else
+ {
+ usrerr("Illegal body type %s", CurEnv->e_bodytype);
+ CurEnv->e_bodytype = NULL;
+ }
+
+ /* tweak default DSN notifications */
+ if (DefaultNotify == 0)
+ DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
+
+ /* be sure we don't pick up bogus HOSTALIASES environment variable */
+ if (queuemode && RealUid != 0)
+ (void) unsetenv("HOSTALIASES");
+
+ /* check for sane configuration level */
+ if (ConfigLevel > MAXCONFIGLEVEL)
+ {
+ syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
+ ConfigLevel, Version, MAXCONFIGLEVEL);
+ }
+
+ /* need MCI cache to have persistence */
+ if (HostStatDir != NULL && MaxMciCache == 0)
+ {
+ HostStatDir = NULL;
+ printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
+ }
+
+ /* need HostStatusDir in order to have SingleThreadDelivery */
+ if (SingleThreadDelivery && HostStatDir == NULL)
+ {
+ SingleThreadDelivery = FALSE;
+ printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
+ }
+
+ /* check for permissions */
+ if ((OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
+ OpMode == MD_PURGESTAT) && RealUid != 0)
+ {
+ if (LogLevel > 1)
+ sm_syslog(LOG_ALERT, NOQID,
+ "user %d attempted to %s",
+ RealUid,
+ OpMode != MD_PURGESTAT ? "run daemon"
+ : "purge host status");
+ usrerr("Permission denied");
+ exit(EX_USAGE);
+ }
+
+ if (MeToo)
+ BlankEnvelope.e_flags |= EF_METOO;
+
+ switch (OpMode)
+ {
+ case MD_TEST:
+ /* don't have persistent host status in test mode */
+ HostStatDir = NULL;
+ if (Verbose == 0)
+ Verbose = 2;
+ CurEnv->e_errormode = EM_PRINT;
+ HoldErrs = FALSE;
+ break;
+
+ case MD_VERIFY:
+ CurEnv->e_errormode = EM_PRINT;
+ HoldErrs = FALSE;
+ /* arrange to exit cleanly on hangup signal */
+ if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
+ setsignal(SIGHUP, intsig);
+ break;
+
+ case MD_FGDAEMON:
+ run_in_foreground = TRUE;
+ OpMode = MD_DAEMON;
+ /* fall through ... */
+
+ case MD_DAEMON:
+ vendor_daemon_setup(CurEnv);
+
+ /* remove things that don't make sense in daemon mode */
+ FullName = NULL;
+ GrabTo = FALSE;
+
+ /* arrange to restart on hangup signal */
+ if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
+ sm_syslog(LOG_WARNING, NOQID,
+ "daemon invoked without full pathname; kill -1 won't work");
+ setsignal(SIGHUP, sighup);
+
+ /* workaround: can't seem to release the signal in the parent */
+ releasesignal(SIGHUP);
+ break;
+
+ case MD_INITALIAS:
+ Verbose = 2;
+ CurEnv->e_errormode = EM_PRINT;
+ HoldErrs = FALSE;
+ /* fall through... */
+
+ case MD_PRINT:
+ /* to handle sendmail -bp -qSfoobar properly */
+ queuemode = FALSE;
+ /* fall through... */
+
+ default:
+ /* arrange to exit cleanly on hangup signal */
+ if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
+ setsignal(SIGHUP, intsig);
+ break;
+ }
+
+ /* special considerations for FullName */
+ if (FullName != NULL)
+ {
+ char *full = NULL;
+ extern bool rfc822_string __P((char *));
+
+ /* full names can't have newlines */
+ if (strchr(FullName, '\n') != NULL)
+ {
+ FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
+ }
+ /* check for characters that may have to be quoted */
+ if (!rfc822_string(FullName))
+ {
+ extern char *addquotes __P((char *));
+
+ /*
+ ** Quote a full name with special characters
+ ** as a comment so crackaddr() doesn't destroy
+ ** the name portion of the address.
+ */
+ FullName = addquotes(FullName);
+ if (full != NULL)
+ free(full);
+ }
+ }
+
+ /* do heuristic mode adjustment */
+ if (Verbose)
+ {
+ /* turn off noconnect option */
+ setoption('c', "F", TRUE, FALSE, CurEnv);
+
+ /* turn on interactive delivery */
+ setoption('d', "", TRUE, FALSE, CurEnv);
+ }
+
+ /* check for out of date configuration level */
+ if (ConfigLevel < MAXCONFIGLEVEL)
+ {
+ message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
+ Version, MAXCONFIGLEVEL, ConfigLevel);
+ }
+
+ if (ConfigLevel < 3)
+ {
+ UseErrorsTo = TRUE;
+ }
+
+ /* set options that were previous macros */
+ if (SmtpGreeting == NULL)
+ {
+ if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
+ SmtpGreeting = newstr(p);
+ else
+ SmtpGreeting = "\201j Sendmail \201v ready at \201b";
+ }
+ if (UnixFromLine == NULL)
+ {
+ if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
+ UnixFromLine = newstr(p);
+ else
+ UnixFromLine = "From \201g \201d";
+ }
+
+ /* our name for SMTP codes */
+ expand("\201j", jbuf, sizeof jbuf, CurEnv);
+ MyHostName = jbuf;
+ if (strchr(jbuf, '.') == NULL)
+ message("WARNING: local host name (%s) is not qualified; fix $j in config file",
+ jbuf);
+
+ /* make certain that this name is part of the $=w class */
+ setclass('w', MyHostName);
+
+ /* the indices of built-in mailers */
+ st = stab("local", ST_MAILER, ST_FIND);
+ if (st != NULL)
+ LocalMailer = st->s_mailer;
+ else if (OpMode != MD_TEST || !warn_C_flag)
+ syserr("No local mailer defined");
+
+ st = stab("prog", ST_MAILER, ST_FIND);
+ if (st == NULL)
+ syserr("No prog mailer defined");
+ else
+ {
+ ProgMailer = st->s_mailer;
+ clrbitn(M_MUSER, ProgMailer->m_flags);
+ }
+
+ st = stab("*file*", ST_MAILER, ST_FIND);
+ if (st == NULL)
+ syserr("No *file* mailer defined");
+ else
+ {
+ FileMailer = st->s_mailer;
+ clrbitn(M_MUSER, FileMailer->m_flags);
+ }
+
+ st = stab("*include*", ST_MAILER, ST_FIND);
+ if (st == NULL)
+ syserr("No *include* mailer defined");
+ else
+ InclMailer = st->s_mailer;
+
+ if (ConfigLevel < 6)
+ {
+ /* heuristic tweaking of local mailer for back compat */
+ if (LocalMailer != NULL)
+ {
+ setbitn(M_ALIASABLE, LocalMailer->m_flags);
+ setbitn(M_HASPWENT, LocalMailer->m_flags);
+ setbitn(M_TRYRULESET5, LocalMailer->m_flags);
+ setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
+ setbitn(M_CHECKPROG, LocalMailer->m_flags);
+ setbitn(M_CHECKFILE, LocalMailer->m_flags);
+ setbitn(M_CHECKUDB, LocalMailer->m_flags);
+ }
+ if (ProgMailer != NULL)
+ setbitn(M_RUNASRCPT, ProgMailer->m_flags);
+ if (FileMailer != NULL)
+ setbitn(M_RUNASRCPT, FileMailer->m_flags);
+ }
+ if (ConfigLevel < 7)
+ {
+ if (LocalMailer != NULL)
+ setbitn(M_VRFY250, LocalMailer->m_flags);
+ if (ProgMailer != NULL)
+ setbitn(M_VRFY250, ProgMailer->m_flags);
+ if (FileMailer != NULL)
+ setbitn(M_VRFY250, FileMailer->m_flags);
+ }
+
+ /* MIME Content-Types that cannot be transfer encoded */
+ setclass('n', "multipart/signed");
+
+ /* MIME message/xxx subtypes that can be treated as messages */
+ setclass('s', "rfc822");
+
+ /* MIME Content-Transfer-Encodings that can be encoded */
+ setclass('e', "7bit");
+ setclass('e', "8bit");
+ setclass('e', "binary");
+
+#ifdef USE_B_CLASS
+ /* MIME Content-Types that should be treated as binary */
+ setclass('b', "image");
+ setclass('b', "audio");
+ setclass('b', "video");
+ setclass('b', "application/octet-stream");
+#endif
+
+ /* operate in queue directory */
+ if (QueueDir == NULL)
+ {
+ if (OpMode != MD_TEST)
+ {
+ syserr("QueueDirectory (Q) option must be set");
+ ExitStat = EX_CONFIG;
+ }
+ }
+ else
+ {
+ /* test path to get warning messages */
+ (void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE);
+ if (OpMode != MD_TEST && chdir(QueueDir) < 0)
+ {
+ syserr("cannot chdir(%s)", QueueDir);
+ ExitStat = EX_CONFIG;
+ }
+ }
+
+ /* check host status directory for validity */
+ if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
+ {
+ /* cannot use this value */
+ if (tTd(0, 2))
+ printf("Cannot use HostStatusDirectory = %s: %s\n",
+ HostStatDir, errstring(errno));
+ HostStatDir = NULL;
+ }
+
+# if QUEUE
+ if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
+ {
+ struct stat stbuf;
+
+ /* check to see if we own the queue directory */
+ if (stat(".", &stbuf) < 0)
+ syserr("main: cannot stat %s", QueueDir);
+ if (stbuf.st_uid != RealUid)
+ {
+ /* nope, really a botch */
+ usrerr("You do not have permission to process the queue");
+ exit (EX_NOPERM);
+ }
+ }
+# endif /* QUEUE */
+
+ /* if we've had errors so far, exit now */
+ if (ExitStat != EX_OK && OpMode != MD_TEST)
+ {
+ endpwent();
+ setuid(RealUid);
+ exit(ExitStat);
+ }
+
+#if XDEBUG
+ checkfd012("before main() initmaps");
+#endif
+
+ /*
+ ** Do operation-mode-dependent initialization.
+ */
+
+ switch (OpMode)
+ {
+ case MD_PRINT:
+ /* print the queue */
+#if QUEUE
+ dropenvelope(CurEnv, TRUE);
+ printqueue();
+ endpwent();
+ setuid(RealUid);
+ exit(EX_OK);
+#else /* QUEUE */
+ usrerr("No queue to print");
+ finis();
+#endif /* QUEUE */
+
+ case MD_HOSTSTAT:
+ mci_traverse_persistent(mci_print_persistent, NULL);
+ exit(EX_OK);
+ break;
+
+ case MD_PURGESTAT:
+ mci_traverse_persistent(mci_purge_persistent, NULL);
+ exit(EX_OK);
+ break;
+
+ case MD_INITALIAS:
+ /* initialize alias database */
+ initmaps(TRUE, CurEnv);
+ endpwent();
+ setuid(RealUid);
+ exit(ExitStat);
+
+ case MD_SMTP:
+ case MD_DAEMON:
+ /* reset DSN parameters */
+ DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
+ CurEnv->e_envid = NULL;
+ CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
+
+ /* don't open alias database -- done in srvrsmtp */
+ break;
+
+ default:
+ /* open the alias database */
+ initmaps(FALSE, CurEnv);
+ break;
+ }
+
+ if (tTd(0, 15))
+ {
+ extern void printrules __P((void));
+
+ /* print configuration table (or at least part of it) */
+ if (tTd(0, 90))
+ printrules();
+ for (i = 0; i < MAXMAILERS; i++)
+ {
+ if (Mailer[i] != NULL)
+ printmailer(Mailer[i]);
+ }
+ }
+
+ /*
+ ** Switch to the main envelope.
+ */
+
+ CurEnv = newenvelope(&MainEnvelope, CurEnv);
+ MainEnvelope.e_flags = BlankEnvelope.e_flags;
+
+ /*
+ ** If test mode, read addresses from stdin and process.
+ */
+
+ if (OpMode == MD_TEST)
+ {
+ char buf[MAXLINE];
+ SIGFUNC_DECL intindebug __P((int));
+
+ if (isatty(fileno(stdin)))
+ Verbose = 2;
+
+ if (Verbose)
+ {
+ printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
+ printf("Enter <ruleset> <address>\n");
+ }
+ if (setjmp(TopFrame) > 0)
+ printf("\n");
+ (void) setsignal(SIGINT, intindebug);
+ for (;;)
+ {
+ extern void testmodeline __P((char *, ENVELOPE *));
+
+ if (Verbose == 2)
+ printf("> ");
+ (void) fflush(stdout);
+ if (fgets(buf, sizeof buf, stdin) == NULL)
+ finis();
+ p = strchr(buf, '\n');
+ if (p != NULL)
+ *p = '\0';
+ if (Verbose < 2)
+ printf("> %s\n", buf);
+ testmodeline(buf, CurEnv);
+ }
+ }
+
+# if QUEUE
+ /*
+ ** If collecting stuff from the queue, go start doing that.
+ */
+
+ if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
+ {
+ (void) runqueue(FALSE, Verbose);
+ finis();
+ }
+# endif /* QUEUE */
+
+ /*
+ ** If a daemon, wait for a request.
+ ** getrequests will always return in a child.
+ ** If we should also be processing the queue, start
+ ** doing it in background.
+ ** We check for any errors that might have happened
+ ** during startup.
+ */
+
+ if (OpMode == MD_DAEMON || QueueIntvl != 0)
+ {
+ char dtype[200];
+ extern void getrequests __P((ENVELOPE *));
+
+ if (!run_in_foreground && !tTd(99, 100))
+ {
+ /* put us in background */
+ i = fork();
+ if (i < 0)
+ syserr("daemon: cannot fork");
+ if (i != 0)
+ exit(EX_OK);
+
+ /* disconnect from our controlling tty */
+ disconnect(2, CurEnv);
+ }
+
+ dtype[0] = '\0';
+ if (OpMode == MD_DAEMON)
+ strcat(dtype, "+SMTP");
+ if (QueueIntvl != 0)
+ {
+ strcat(dtype, "+queueing@");
+ strcat(dtype, pintvl(QueueIntvl, TRUE));
+ }
+ if (tTd(0, 1))
+ strcat(dtype, "+debugging");
+
+ sm_syslog(LOG_INFO, NOQID,
+ "starting daemon (%s): %s", Version, dtype + 1);
+#ifdef XLA
+ xla_create_file();
+#endif
+
+# if QUEUE
+ if (queuemode)
+ {
+ (void) runqueue(TRUE, FALSE);
+ if (OpMode != MD_DAEMON)
+ {
+ for (;;)
+ {
+ pause();
+ if (DoQueueRun)
+ (void) runqueue(TRUE, FALSE);
+ }
+ }
+ }
+# endif /* QUEUE */
+ dropenvelope(CurEnv, TRUE);
+
+#if DAEMON
+ getrequests(CurEnv);
+
+ /* drop privileges */
+ (void) drop_privileges(FALSE);
+
+ /* at this point we are in a child: reset state */
+ (void) newenvelope(CurEnv, CurEnv);
+
+ /*
+ ** Get authentication data
+ */
+
+ p = getauthinfo(fileno(InChannel), &forged);
+ define('_', p, &BlankEnvelope);
+#endif /* DAEMON */
+ }
+
+# if SMTP
+ /*
+ ** If running SMTP protocol, start collecting and executing
+ ** commands. This will never return.
+ */
+
+ if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
+ {
+ char pbuf[20];
+ extern void smtp __P((char *, ENVELOPE *));
+
+ /*
+ ** Save some macros for check_* rulesets.
+ */
+
+ if (forged)
+ {
+ char ipbuf[103];
+
+ snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
+ inet_ntoa(RealHostAddr.sin.sin_addr));
+
+ define(macid("{client_name}", NULL),
+ newstr(ipbuf), &BlankEnvelope);
+ }
+ else
+ define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope);
+ define(macid("{client_addr}", NULL),
+ newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
+ if (RealHostAddr.sa.sa_family == AF_INET)
+ snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port);
+ else
+ snprintf(pbuf, sizeof pbuf, "0");
+ define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope);
+
+ if (OpMode == MD_DAEMON)
+ {
+ /* validate the connection */
+ HoldErrs = TRUE;
+ nullserver = validate_connection(&RealHostAddr,
+ RealHostName, CurEnv);
+ HoldErrs = FALSE;
+ }
+ smtp(nullserver, CurEnv);
+ }
+# endif /* SMTP */
+
+ clearenvelope(CurEnv, FALSE);
+ if (OpMode == MD_VERIFY)
+ {
+ CurEnv->e_sendmode = SM_VERIFY;
+ PostMasterCopy = NULL;
+ }
+ else
+ {
+ /* interactive -- all errors are global */
+ CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
+ }
+
+ /*
+ ** Do basic system initialization and set the sender
+ */
+
+ initsys(CurEnv);
+ if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
+ auth_warning(CurEnv, "%s set sender to %s using -%c",
+ RealUserName, from, warn_f_flag);
+ setsender(from, CurEnv, NULL, '\0', FALSE);
+ if (macvalue('s', CurEnv) == NULL)
+ define('s', RealHostName, CurEnv);
+
+ if (*av == NULL && !GrabTo)
+ {
+ CurEnv->e_flags |= EF_GLOBALERRS;
+ usrerr("Recipient names must be specified");
+
+ /* collect body for UUCP return */
+ if (OpMode != MD_VERIFY)
+ collect(InChannel, FALSE, NULL, CurEnv);
+ finis();
+ }
+
+ /*
+ ** Scan argv and deliver the message to everyone.
+ */
+
+ sendtoargv(av, CurEnv);
+
+ /* if we have had errors sofar, arrange a meaningful exit stat */
+ if (Errors > 0 && ExitStat == EX_OK)
+ ExitStat = EX_USAGE;
+
+#if _FFR_FIX_DASHT
+ /*
+ ** If using -t, force not sending to argv recipients, even
+ ** if they are mentioned in the headers.
+ */
+
+ if (GrabTo)
+ {
+ ADDRESS *q;
+
+ for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
+ q->q_flags |= QDONTSEND;
+ }
+#endif
+
+ /*
+ ** Read the input mail.
+ */
+
+ CurEnv->e_to = NULL;
+ if (OpMode != MD_VERIFY || GrabTo)
+ {
+ long savedflags = CurEnv->e_flags & EF_FATALERRS;
+
+ CurEnv->e_flags |= EF_GLOBALERRS;
+ CurEnv->e_flags &= ~EF_FATALERRS;
+ collect(InChannel, FALSE, NULL, CurEnv);
+
+ /* bail out if message too large */
+ if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
+ {
+ finis();
+ /*NOTREACHED*/
+ return -1;
+ }
+ CurEnv->e_flags |= savedflags;
+ }
+ errno = 0;
+
+ if (tTd(1, 1))
+ printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
+
+ /*
+ ** Actually send everything.
+ ** If verifying, just ack.
+ */
+
+ CurEnv->e_from.q_flags |= QDONTSEND;
+ if (tTd(1, 5))
+ {
+ printf("main: QDONTSEND ");
+ printaddr(&CurEnv->e_from, FALSE);
+ }
+ CurEnv->e_to = NULL;
+ CurrentLA = getla();
+ GrabTo = FALSE;
+ sendall(CurEnv, SM_DEFAULT);
+
+ /*
+ ** All done.
+ ** Don't send return error message if in VERIFY mode.
+ */
+
+ finis();
+ /*NOTREACHED*/
+ return -1;
+}
+
+
+/* ARGSUSED */
+SIGFUNC_DECL
+intindebug(sig)
+ int sig;
+{
+ longjmp(TopFrame, 1);
+ return SIGFUNC_RETURN;
+}
+
+
+ /*
+** FINIS -- Clean up and exit.
+**
+** Parameters:
+** none
+**
+** Returns:
+** never
+**
+** Side Effects:
+** exits sendmail
+*/
+
+void
+finis()
+{
+ if (tTd(2, 1))
+ {
+ extern void printenvflags __P((ENVELOPE *));
+
+ printf("\n====finis: stat %d e_id=%s e_flags=",
+ ExitStat,
+ CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
+ printenvflags(CurEnv);
+ }
+ if (tTd(2, 9))
+ printopenfds(FALSE);
+
+ /* if we fail in finis(), just exit */
+ if (setjmp(TopFrame) != 0)
+ {
+ /* failed -- just give it up */
+ goto forceexit;
+ }
+
+ /* clean up temp files */
+ CurEnv->e_to = NULL;
+ if (CurEnv->e_id != NULL)
+ dropenvelope(CurEnv, TRUE);
+
+ /* flush any cached connections */
+ mci_flush(TRUE, NULL);
+
+# ifdef XLA
+ /* clean up extended load average stuff */
+ xla_all_end();
+# endif
+
+ /* and exit */
+ forceexit:
+ if (LogLevel > 78)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "finis, pid=%d",
+ getpid());
+ if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
+ ExitStat = EX_OK;
+
+ /* reset uid for process accounting */
+ endpwent();
+ setuid(RealUid);
+
+ exit(ExitStat);
+}
+ /*
+** INTSIG -- clean up on interrupt
+**
+** This just arranges to exit. It pessimises in that it
+** may resend a message.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Unlocks the current job.
+*/
+
+/* ARGSUSED */
+SIGFUNC_DECL
+intsig(sig)
+ int sig;
+{
+ if (LogLevel > 79)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
+ FileName = NULL;
+ unlockqueue(CurEnv);
+#ifdef XLA
+ xla_all_end();
+#endif
+
+ /* reset uid for process accounting */
+ endpwent();
+ setuid(RealUid);
+
+ exit(EX_OK);
+}
+ /*
+** INITMACROS -- initialize the macro system
+**
+** This just involves defining some macros that are actually
+** used internally as metasymbols to be themselves.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** initializes several macros to be themselves.
+*/
+
+struct metamac MetaMacros[] =
+{
+ /* LHS pattern matching characters */
+ { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE },
+ { '=', MATCHCLASS }, { '~', MATCHNCLASS },
+
+ /* these are RHS metasymbols */
+ { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER },
+ { '>', CALLSUBR },
+
+ /* the conditional operations */
+ { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI },
+
+ /* the hostname lookup characters */
+ { '[', HOSTBEGIN }, { ']', HOSTEND },
+ { '(', LOOKUPBEGIN }, { ')', LOOKUPEND },
+
+ /* miscellaneous control characters */
+ { '&', MACRODEXPAND },
+
+ { '\0' }
+};
+
+#define MACBINDING(name, mid) \
+ stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
+ MacroName[mid] = name;
+
+void
+initmacros(e)
+ register ENVELOPE *e;
+{
+ register struct metamac *m;
+ register int c;
+ char buf[5];
+ extern char *MacroName[256];
+
+ for (m = MetaMacros; m->metaname != '\0'; m++)
+ {
+ buf[0] = m->metaval;
+ buf[1] = '\0';
+ define(m->metaname, newstr(buf), e);
+ }
+ buf[0] = MATCHREPL;
+ buf[2] = '\0';
+ for (c = '0'; c <= '9'; c++)
+ {
+ buf[1] = c;
+ define(c, newstr(buf), e);
+ }
+
+ /* set defaults for some macros sendmail will use later */
+ define('n', "MAILER-DAEMON", e);
+
+ /* set up external names for some internal macros */
+ MACBINDING("opMode", MID_OPMODE);
+ /*XXX should probably add equivalents for all short macros here XXX*/
+}
+ /*
+** DISCONNECT -- remove our connection with any foreground process
+**
+** Parameters:
+** droplev -- how "deeply" we should drop the line.
+** 0 -- ignore signals, mail back errors, make sure
+** output goes to stdout.
+** 1 -- also, make stdout go to transcript.
+** 2 -- also, disconnect from controlling terminal
+** (only for daemon mode).
+** e -- the current envelope.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Trys to insure that we are immune to vagaries of
+** the controlling tty.
+*/
+
+void
+disconnect(droplev, e)
+ int droplev;
+ register ENVELOPE *e;
+{
+ int fd;
+
+ if (tTd(52, 1))
+ printf("disconnect: In %d Out %d, e=%lx\n",
+ fileno(InChannel), fileno(OutChannel), (u_long) e);
+ if (tTd(52, 100))
+ {
+ printf("don't\n");
+ return;
+ }
+ if (LogLevel > 93)
+ sm_syslog(LOG_DEBUG, e->e_id,
+ "disconnect level %d",
+ droplev);
+
+ /* be sure we don't get nasty signals */
+ (void) setsignal(SIGINT, SIG_IGN);
+ (void) setsignal(SIGQUIT, SIG_IGN);
+
+ /* we can't communicate with our caller, so.... */
+ HoldErrs = TRUE;
+ CurEnv->e_errormode = EM_MAIL;
+ Verbose = 0;
+ DisConnected = TRUE;
+
+ /* all input from /dev/null */
+ if (InChannel != stdin)
+ {
+ (void) fclose(InChannel);
+ InChannel = stdin;
+ }
+ if (freopen("/dev/null", "r", stdin) == NULL)
+ sm_syslog(LOG_ERR, e->e_id,
+ "disconnect: freopen(\"/dev/null\") failed: %s",
+ errstring(errno));
+
+ /* output to the transcript */
+ if (OutChannel != stdout)
+ {
+ (void) fclose(OutChannel);
+ OutChannel = stdout;
+ }
+ if (droplev > 0)
+ {
+ if (e->e_xfp == NULL)
+ {
+ fd = open("/dev/null", O_WRONLY, 0666);
+ if (fd == -1)
+ sm_syslog(LOG_ERR, e->e_id,
+ "disconnect: open(\"/dev/null\") failed: %s",
+ errstring(errno));
+ }
+ else
+ {
+ fd = fileno(e->e_xfp);
+ if (fd == -1)
+ sm_syslog(LOG_ERR, e->e_id,
+ "disconnect: fileno(e->e_xfp) failed: %s",
+ errstring(errno));
+ }
+ (void) fflush(stdout);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (e->e_xfp == NULL)
+ close(fd);
+ }
+
+ /* drop our controlling TTY completely if possible */
+ if (droplev > 1)
+ {
+ (void) setsid();
+ errno = 0;
+ }
+
+#if XDEBUG
+ checkfd012("disconnect");
+#endif
+
+ if (LogLevel > 71)
+ sm_syslog(LOG_DEBUG, e->e_id,
+ "in background, pid=%d",
+ getpid());
+
+ errno = 0;
+}
+
+static void
+obsolete(argv)
+ char *argv[];
+{
+ register char *ap;
+ register char *op;
+
+ while ((ap = *++argv) != NULL)
+ {
+ /* Return if "--" or not an option of any form. */
+ if (ap[0] != '-' || ap[1] == '-')
+ return;
+
+ /* skip over options that do have a value */
+ op = strchr(OPTIONS, ap[1]);
+ if (op != NULL && *++op == ':' && ap[2] == '\0' &&
+ ap[1] != 'd' &&
+#if defined(sony_news)
+ ap[1] != 'E' && ap[1] != 'J' &&
+#endif
+ argv[1] != NULL && argv[1][0] != '-')
+ {
+ argv++;
+ continue;
+ }
+
+ /* If -C doesn't have an argument, use sendmail.cf. */
+#define __DEFPATH "sendmail.cf"
+ if (ap[1] == 'C' && ap[2] == '\0')
+ {
+ *argv = xalloc(sizeof(__DEFPATH) + 2);
+ argv[0][0] = '-';
+ argv[0][1] = 'C';
+ (void)strcpy(&argv[0][2], __DEFPATH);
+ }
+
+ /* If -q doesn't have an argument, run it once. */
+ if (ap[1] == 'q' && ap[2] == '\0')
+ *argv = "-q0";
+
+ /* if -d doesn't have an argument, use 0-99.1 */
+ if (ap[1] == 'd' && ap[2] == '\0')
+ *argv = "-d0-99.1";
+
+# if defined(sony_news)
+ /* if -E doesn't have an argument, use -EC */
+ if (ap[1] == 'E' && ap[2] == '\0')
+ *argv = "-EC";
+
+ /* if -J doesn't have an argument, use -JJ */
+ if (ap[1] == 'J' && ap[2] == '\0')
+ *argv = "-JJ";
+# endif
+ }
+}
+ /*
+** AUTH_WARNING -- specify authorization warning
+**
+** Parameters:
+** e -- the current envelope.
+** msg -- the text of the message.
+** args -- arguments to the message.
+**
+** Returns:
+** none.
+*/
+
+void
+#ifdef __STDC__
+auth_warning(register ENVELOPE *e, const char *msg, ...)
+#else
+auth_warning(e, msg, va_alist)
+ register ENVELOPE *e;
+ const char *msg;
+ va_dcl
+#endif
+{
+ char buf[MAXLINE];
+ VA_LOCAL_DECL
+
+ if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
+ {
+ register char *p;
+ static char hostbuf[48];
+ extern struct hostent *myhostname __P((char *, int));
+
+ if (hostbuf[0] == '\0')
+ (void) myhostname(hostbuf, sizeof hostbuf);
+
+ (void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
+ p = &buf[strlen(buf)];
+ VA_START(msg);
+ vsnprintf(p, SPACELEFT(buf, p), msg, ap);
+ VA_END;
+ addheader("X-Authentication-Warning", buf, &e->e_header);
+ if (LogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Authentication-Warning: %.400s",
+ buf);
+ }
+}
+ /*
+** GETEXTENV -- get from external environment
+**
+** Parameters:
+** envar -- the name of the variable to retrieve
+**
+** Returns:
+** The value, if any.
+*/
+
+char *
+getextenv(envar)
+ const char *envar;
+{
+ char **envp;
+ int l;
+
+ l = strlen(envar);
+ for (envp = ExternalEnviron; *envp != NULL; envp++)
+ {
+ if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
+ return &(*envp)[l + 1];
+ }
+ return NULL;
+}
+ /*
+** SETUSERENV -- set an environment in the propogated environment
+**
+** Parameters:
+** envar -- the name of the environment variable.
+** value -- the value to which it should be set. If
+** null, this is extracted from the incoming
+** environment. If that is not set, the call
+** to setuserenv is ignored.
+**
+** Returns:
+** none.
+*/
+
+void
+setuserenv(envar, value)
+ const char *envar;
+ const char *value;
+{
+ int i;
+ char **evp = UserEnviron;
+ char *p;
+
+ if (value == NULL)
+ {
+ value = getextenv(envar);
+ if (value == NULL)
+ return;
+ }
+
+ i = strlen(envar);
+ p = (char *) xalloc(strlen(value) + i + 2);
+ strcpy(p, envar);
+ p[i++] = '=';
+ strcpy(&p[i], value);
+
+ while (*evp != NULL && strncmp(*evp, p, i) != 0)
+ evp++;
+ if (*evp != NULL)
+ {
+ *evp++ = p;
+ }
+ else if (evp < &UserEnviron[MAXUSERENVIRON])
+ {
+ *evp++ = p;
+ *evp = NULL;
+ }
+
+ /* make sure it is in our environment as well */
+ if (putenv(p) < 0)
+ syserr("setuserenv: putenv(%s) failed", p);
+}
+ /*
+** DUMPSTATE -- dump state
+**
+** For debugging.
+*/
+
+void
+dumpstate(when)
+ char *when;
+{
+ register char *j = macvalue('j', CurEnv);
+ int rs;
+
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "--- dumping state on %s: $j = %s ---",
+ when,
+ j == NULL ? "<NULL>" : j);
+ if (j != NULL)
+ {
+ if (!wordinclass(j, 'w'))
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "*** $j not in $=w ***");
+ }
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
+ printopenfds(TRUE);
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
+ mci_dump_all(TRUE);
+ rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
+ if (rs > 0)
+ {
+ int stat;
+ register char **pvp;
+ char *pv[MAXATOM + 1];
+
+ pv[0] = NULL;
+ stat = rewrite(pv, rs, 0, CurEnv);
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "--- ruleset debug_dumpstate returns stat %d, pv: ---",
+ stat);
+ for (pvp = pv; *pvp != NULL; pvp++)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
+ }
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
+}
+
+
+/* ARGSUSED */
+SIGFUNC_DECL
+sigusr1(sig)
+ int sig;
+{
+ dumpstate("user signal");
+ return SIGFUNC_RETURN;
+}
+
+
+/* ARGSUSED */
+SIGFUNC_DECL
+sighup(sig)
+ int sig;
+{
+ if (SaveArgv[0][0] != '/')
+ {
+ if (LogLevel > 3)
+ sm_syslog(LOG_INFO, NOQID, "could not restart: need full path");
+ exit(EX_OSFILE);
+ }
+ if (LogLevel > 3)
+ sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]);
+ alarm(0);
+ releasesignal(SIGHUP);
+ if (drop_privileges(TRUE) != EX_OK)
+ {
+ if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m",
+ RunAsUid, RunAsGid);
+ exit(EX_OSERR);
+ }
+ execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
+ exit(EX_OSFILE);
+}
+ /*
+** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
+**
+** Parameters:
+** to_real_uid -- if set, drop to the real uid instead
+** of the RunAsUser.
+**
+** Returns:
+** EX_OSERR if the setuid failed.
+** EX_OK otherwise.
+*/
+
+int
+drop_privileges(to_real_uid)
+ bool to_real_uid;
+{
+ int rval = EX_OK;
+ GIDSET_T emptygidset[1];
+
+ if (tTd(47, 1))
+ printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
+ (int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid);
+
+ if (to_real_uid)
+ {
+ RunAsUserName = RealUserName;
+ RunAsUid = RealUid;
+ RunAsGid = RealGid;
+ }
+
+ /* make sure no one can grab open descriptors for secret files */
+ endpwent();
+
+ /* reset group permissions; these can be set later */
+ emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
+ if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
+ rval = EX_OSERR;
+
+ /* reset primary group and user id */
+ if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
+ rval = EX_OSERR;
+ if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
+ rval = EX_OSERR;
+ if (tTd(47, 5))
+ {
+ printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
+ (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
+ printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
+ }
+ return rval;
+}
+ /*
+** FILL_FD -- make sure a file descriptor has been properly allocated
+**
+** Used to make sure that stdin/out/err are allocated on startup
+**
+** Parameters:
+** fd -- the file descriptor to be filled.
+** where -- a string used for logging. If NULL, this is
+** being called on startup, and logging should
+** not be done.
+**
+** Returns:
+** none
+*/
+
+void
+fill_fd(fd, where)
+ int fd;
+ char *where;
+{
+ int i;
+ struct stat stbuf;
+
+ if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
+ return;
+
+ if (where != NULL)
+ syserr("fill_fd: %s: fd %d not open", where, fd);
+ else
+ MissingFds |= 1 << fd;
+ i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
+ if (i < 0)
+ {
+ syserr("!fill_fd: %s: cannot open /dev/null",
+ where == NULL ? "startup" : where);
+ }
+ if (fd != i)
+ {
+ (void) dup2(i, fd);
+ (void) close(i);
+ }
+}
+ /*
+** TESTMODELINE -- process a test mode input line
+**
+** Parameters:
+** line -- the input line.
+** e -- the current environment.
+** Syntax:
+** # a comment
+** .X process X as a configuration line
+** =X dump a configuration item (such as mailers)
+** $X dump a macro or class
+** /X try an activity
+** X normal process through rule set X
+*/
+
+void
+testmodeline(line, e)
+ char *line;
+ ENVELOPE *e;
+{
+ register char *p;
+ char *q;
+ auto char *delimptr;
+ int mid;
+ int i, rs;
+ STAB *map;
+ char **s;
+ struct rewrite *rw;
+ ADDRESS a;
+ static int tryflags = RF_COPYNONE;
+ char exbuf[MAXLINE];
+ extern bool invalidaddr __P((char *, char *));
+ extern char *crackaddr __P((char *));
+ extern void dump_class __P((STAB *, int));
+ extern void translate_dollars __P((char *));
+ extern void help __P((char *));
+
+ switch (line[0])
+ {
+ case '#':
+ case 0:
+ return;
+
+ case '?':
+ help("-bt");
+ return;
+
+ case '.': /* config-style settings */
+ switch (line[1])
+ {
+ case 'D':
+ mid = macid(&line[2], &delimptr);
+ if (mid == '\0')
+ return;
+ translate_dollars(delimptr);
+ define(mid, newstr(delimptr), e);
+ break;
+
+ case 'C':
+ if (line[2] == '\0') /* not to call syserr() */
+ return;
+
+ mid = macid(&line[2], &delimptr);
+ if (mid == '\0')
+ return;
+ translate_dollars(delimptr);
+ expand(delimptr, exbuf, sizeof exbuf, e);
+ p = exbuf;
+ while (*p != '\0')
+ {
+ register char *wd;
+ char delim;
+
+ while (*p != '\0' && isascii(*p) && isspace(*p))
+ p++;
+ wd = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ delim = *p;
+ *p = '\0';
+ if (wd[0] != '\0')
+ setclass(mid, wd);
+ *p = delim;
+ }
+ break;
+
+ case '\0':
+ printf("Usage: .[DC]macro value(s)\n");
+ break;
+
+ default:
+ printf("Unknown \".\" command %s\n", line);
+ break;
+ }
+ return;
+
+ case '=': /* config-style settings */
+ switch (line[1])
+ {
+ case 'S': /* dump rule set */
+ rs = strtorwset(&line[2], NULL, ST_FIND);
+ if (rs < 0)
+ {
+ printf("Undefined ruleset %s\n", &line[2]);
+ return;
+ }
+ rw = RewriteRules[rs];
+ if (rw == NULL)
+ return;
+ do
+ {
+ putchar('R');
+ s = rw->r_lhs;
+ while (*s != NULL)
+ {
+ xputs(*s++);
+ putchar(' ');
+ }
+ putchar('\t');
+ putchar('\t');
+ s = rw->r_rhs;
+ while (*s != NULL)
+ {
+ xputs(*s++);
+ putchar(' ');
+ }
+ putchar('\n');
+ } while ((rw = rw->r_next) != NULL);
+ break;
+
+ case 'M':
+ for (i = 0; i < MAXMAILERS; i++)
+ {
+ if (Mailer[i] != NULL)
+ printmailer(Mailer[i]);
+ }
+ break;
+
+ case '\0':
+ printf("Usage: =Sruleset or =M\n");
+ break;
+
+ default:
+ printf("Unknown \"=\" command %s\n", line);
+ break;
+ }
+ return;
+
+ case '-': /* set command-line-like opts */
+ switch (line[1])
+ {
+ case 'd':
+ tTflag(&line[2]);
+ break;
+
+ case '\0':
+ printf("Usage: -d{debug arguments}\n");
+ break;
+
+ default:
+ printf("Unknown \"-\" command %s\n", line);
+ break;
+ }
+ return;
+
+ case '$':
+ if (line[1] == '=')
+ {
+ mid = macid(&line[2], NULL);
+ if (mid != '\0')
+ stabapply(dump_class, mid);
+ return;
+ }
+ mid = macid(&line[1], NULL);
+ if (mid == '\0')
+ return;
+ p = macvalue(mid, e);
+ if (p == NULL)
+ printf("Undefined\n");
+ else
+ {
+ xputs(p);
+ printf("\n");
+ }
+ return;
+
+ case '/': /* miscellaneous commands */
+ p = &line[strlen(line)];
+ while (--p >= line && isascii(*p) && isspace(*p))
+ *p = '\0';
+ p = strpbrk(line, " \t");
+ if (p != NULL)
+ {
+ while (isascii(*p) && isspace(*p))
+ *p++ = '\0';
+ }
+ else
+ p = "";
+ if (line[1] == '\0')
+ {
+ printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
+ return;
+ }
+ if (strcasecmp(&line[1], "mx") == 0)
+ {
+#if NAMED_BIND
+ /* look up MX records */
+ int nmx;
+ auto int rcode;
+ char *mxhosts[MAXMXHOSTS + 1];
+
+ if (*p == '\0')
+ {
+ printf("Usage: /mx address\n");
+ return;
+ }
+ nmx = getmxrr(p, mxhosts, FALSE, &rcode);
+ printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
+ for (i = 0; i < nmx; i++)
+ printf("\t%s\n", mxhosts[i]);
+#else
+ printf("No MX code compiled in\n");
+#endif
+ }
+ else if (strcasecmp(&line[1], "canon") == 0)
+ {
+ char host[MAXHOSTNAMELEN];
+
+ if (*p == '\0')
+ {
+ printf("Usage: /canon address\n");
+ return;
+ }
+ else if (strlen(p) >= sizeof host)
+ {
+ printf("Name too long\n");
+ return;
+ }
+ strcpy(host, p);
+ (void) getcanonname(host, sizeof(host), HasWildcardMX);
+ printf("getcanonname(%s) returns %s\n", p, host);
+ }
+ else if (strcasecmp(&line[1], "map") == 0)
+ {
+ auto int rcode = EX_OK;
+ char *av[2];
+
+ if (*p == '\0')
+ {
+ printf("Usage: /map mapname key\n");
+ return;
+ }
+ for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
+ continue;
+ if (*q == '\0')
+ {
+ printf("No key specified\n");
+ return;
+ }
+ *q++ = '\0';
+ map = stab(p, ST_MAP, ST_FIND);
+ if (map == NULL)
+ {
+ printf("Map named \"%s\" not found\n", p);
+ return;
+ }
+ if (!bitset(MF_OPEN, map->s_map.map_mflags))
+ {
+ printf("Map named \"%s\" not open\n", p);
+ return;
+ }
+ printf("map_lookup: %s (%s) ", p, q);
+ av[0] = q;
+ av[1] = NULL;
+ p = (*map->s_map.map_class->map_lookup)
+ (&map->s_map, q, av, &rcode);
+ if (p == NULL)
+ printf("no match (%d)\n", rcode);
+ else
+ printf("returns %s (%d)\n", p, rcode);
+ }
+ else if (strcasecmp(&line[1], "try") == 0)
+ {
+ MAILER *m;
+ STAB *s;
+ auto int rcode = EX_OK;
+
+ q = strpbrk(p, " \t");
+ if (q != NULL)
+ {
+ while (isascii(*q) && isspace(*q))
+ *q++ = '\0';
+ }
+ if (q == NULL || *q == '\0')
+ {
+ printf("Usage: /try mailer address\n");
+ return;
+ }
+ s = stab(p, ST_MAILER, ST_FIND);
+ if (s == NULL)
+ {
+ printf("Unknown mailer %s\n", p);
+ return;
+ }
+ m = s->s_mailer;
+ printf("Trying %s %s address %s for mailer %s\n",
+ bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
+ bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
+ q, p);
+ p = remotename(q, m, tryflags, &rcode, CurEnv);
+ printf("Rcode = %d, addr = %s\n",
+ rcode, p == NULL ? "<NULL>" : p);
+ e->e_to = NULL;
+ }
+ else if (strcasecmp(&line[1], "tryflags") == 0)
+ {
+ if (*p == '\0')
+ {
+ printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
+ return;
+ }
+ for (; *p != '\0'; p++)
+ {
+ switch (*p)
+ {
+ case 'H':
+ case 'h':
+ tryflags |= RF_HEADERADDR;
+ break;
+
+ case 'E':
+ case 'e':
+ tryflags &= ~RF_HEADERADDR;
+ break;
+
+ case 'S':
+ case 's':
+ tryflags |= RF_SENDERADDR;
+ break;
+
+ case 'R':
+ case 'r':
+ tryflags &= ~RF_SENDERADDR;
+ break;
+ }
+ }
+ }
+ else if (strcasecmp(&line[1], "parse") == 0)
+ {
+ if (*p == '\0')
+ {
+ printf("Usage: /parse address\n");
+ return;
+ }
+ q = crackaddr(p);
+ printf("Cracked address = ");
+ xputs(q);
+ printf("\nParsing %s %s address\n",
+ bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
+ bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
+ if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
+ printf("Cannot parse\n");
+ else if (a.q_host != NULL && a.q_host[0] != '\0')
+ printf("mailer %s, host %s, user %s\n",
+ a.q_mailer->m_name, a.q_host, a.q_user);
+ else
+ printf("mailer %s, user %s\n",
+ a.q_mailer->m_name, a.q_user);
+ e->e_to = NULL;
+ }
+ else
+ {
+ printf("Unknown \"/\" command %s\n", line);
+ }
+ return;
+ }
+
+ for (p = line; isascii(*p) && isspace(*p); p++)
+ continue;
+ q = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p == '\0')
+ {
+ printf("No address!\n");
+ return;
+ }
+ *p = '\0';
+ if (invalidaddr(p + 1, NULL))
+ return;
+ do
+ {
+ register char **pvp;
+ char pvpbuf[PSBUFSIZE];
+
+ pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
+ &delimptr, NULL);
+ if (pvp == NULL)
+ continue;
+ p = q;
+ while (*p != '\0')
+ {
+ int stat;
+
+ rs = strtorwset(p, NULL, ST_FIND);
+ if (rs < 0)
+ {
+ printf("Undefined ruleset %s\n", p);
+ break;
+ }
+ stat = rewrite(pvp, rs, 0, e);
+ if (stat != EX_OK)
+ printf("== Ruleset %s (%d) status %d\n",
+ p, rs, stat);
+ while (*p != '\0' && *p++ != ',')
+ continue;
+ }
+ } while (*(p = delimptr) != '\0');
+}
+
+
+void
+dump_class(s, id)
+ register STAB *s;
+ int id;
+{
+ if (s->s_type != ST_CLASS)
+ return;
+ if (bitnset(id & 0xff, s->s_class))
+ printf("%s\n", s->s_name);
+}
diff --git a/contrib/sendmail/src/makesendmail b/contrib/sendmail/src/makesendmail
new file mode 100755
index 0000000..ab8a49d
--- /dev/null
+++ b/contrib/sendmail/src/makesendmail
@@ -0,0 +1,513 @@
+#!/bin/sh
+
+# Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+# Copyright (c) 1993, 1996-1997 Eric P. Allman. All rights reserved.
+# Copyright (c) 1993
+# The Regents of the University of California. 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.
+#
+#
+# @(#)Build 8.93 (Berkeley) 6/24/98
+#
+
+#
+# A quick-and-dirty script to compile sendmail and related programs
+# in the presence of multiple architectures. To use, just use
+# "sh Build".
+#
+
+trap "rm -f $obj/.settings$$; exit" 1 2 3 15
+
+cflag=""
+mflag=""
+sflag=""
+makeargs=""
+libdirs=""
+incdirs=""
+libsrch=""
+siteconfig=""
+EX_USAGE=64
+EX_NOINPUT=66
+EX_UNAVAILABLE=69
+
+while [ ! -z "$1" ]
+do
+ case $1
+ in
+ -c) # clean out existing $obj tree
+ cflag=1
+ shift
+ ;;
+
+ -m) # show Makefile name only
+ mflag=1
+ shift
+ ;;
+
+ -E*) # environment variables to pass into Build
+ arg=`echo $1 | sed 's/^-E//'`
+ if [ -z "$arg" ]
+ then
+ shift # move to argument
+ arg=$1
+ fi
+ if [ -z "$arg" ]
+ then
+ echo "Empty -E flag" >&2
+ exit $EX_USAGE
+ else
+ case $arg
+ in
+ *=*) # check format
+ eval $arg
+ export `echo $arg | sed 's;=.*;;'`
+ ;;
+ *) # bad format
+ echo "Bad format for -E argument ($arg)" >&2
+ exit $EX_USAGE
+ ;;
+ esac
+ shift
+ fi
+ ;;
+
+ -L*) # set up LIBDIRS
+ libdirs="$libdirs $1"
+ shift
+ ;;
+
+ -I*) # set up INCDIRS
+ incdirs="$incdirs $1"
+ shift
+ ;;
+
+ -f*) # select site config file
+ arg=`echo $1 | sed 's/^-f//'`
+ if [ -z "$arg" ]
+ then
+ shift # move to argument
+ arg=$1
+ fi
+ if [ "$siteconfig" ]
+ then
+ echo "Only one -f flag allowed" >&2
+ exit $EX_USAGE
+ else
+ siteconfig=$arg
+ if [ -z "$siteconfig" ]
+ then
+ echo "Missing argument for -f flag" >&2
+ exit $EX_USAGE
+ elif [ ! -f "$siteconfig" ]
+ then
+ echo "${siteconfig}: File not found"
+ exit $EX_NOINPUT
+ else
+ shift # move past argument
+ fi
+ fi
+ ;;
+
+ -S) # skip auto-configure
+ sflag="-s"
+ shift
+ ;;
+
+ *) # pass argument to make
+ makeargs="$makeargs \"$1\""
+ shift
+ ;;
+ esac
+done
+
+#
+# Do heuristic guesses !ONLY! for machines that do not have uname
+#
+if [ -d /NextApps -a ! -f /bin/uname -a ! -f /usr/bin/uname ]
+then
+ # probably a NeXT box
+ arch=`hostinfo | sed -n 's/.*Processor type: \([^ ]*\).*/\1/p'`
+ os=NeXT
+ rel=`hostinfo | sed -n 's/.*NeXT Mach \([0-9\.]*\).*/\1/p'`
+elif [ -f /usr/sony/bin/machine -a -f /etc/osversion ]
+then
+ # probably a Sony NEWS 4.x
+ os=NEWS-OS
+ rel=`awk '{ print $3}' /etc/osversion`
+ arch=`/usr/sony/bin/machine`
+elif [ -d /usr/omron -a -f /bin/luna ]
+then
+ # probably a Omron LUNA
+ os=LUNA
+ if [ -f /bin/luna1 ] && /bin/luna1
+ then
+ rel=unios-b
+ arch=luna1
+ elif [ -f /bin/luna2 ] && /bin/luna2
+ then
+ rel=Mach
+ arch=luna2
+ elif [ -f /bin/luna88k ] && /bin/luna88k
+ then
+ rel=Mach
+ arch=luna88k
+ fi
+elif [ -d /usr/apollo -a -d \`node_data ]
+then
+ # probably a Apollo/DOMAIN
+ os=DomainOS
+ arch=$ISP
+ rel=`/usr/apollo/bin/bldt | grep Domain | awk '{ print $4 }' | sed -e 's/,//g'`
+fi
+
+if [ ! "$arch" -a ! "$os" -a ! "$rel" ]
+then
+ arch=`uname -m | sed -e 's/ //g'`
+ os=`uname -s | sed -e 's/\//-/g' -e 's/ //g'`
+ rel=`uname -r | sed -e 's/(/-/g' -e 's/)//g'`
+fi
+
+#
+# Tweak the values we have already got. PLEASE LIMIT THESE to
+# tweaks that are absolutely necessary because your system uname
+# routine doesn't return something sufficiently unique. Don't do
+# it just because you don't like the name that is returned. You
+# can combine the architecture name with the os name to create a
+# unique Makefile name.
+#
+
+# tweak machine architecture
+case $arch
+in
+ sun4*) arch=sun4;;
+
+ 9000/*) arch=`echo $arch | sed -e 's/9000.//' -e 's/..$/xx/'`;;
+
+ DS/907000) arch=ds90;;
+
+ NILE*) arch=NILE
+ os=`uname -v`;;
+esac
+
+# tweak operating system type and release
+node=`uname -n | sed -e 's/\//-/g' -e 's/ //g'`
+if [ "$os" = "$node" -a "$arch" = "i386" -a "$rel" = 3.2 -a "`uname -v`" = 2 ]
+then
+ # old versions of SCO UNIX set uname -s the same as uname -n
+ os=SCO_SV
+fi
+if [ "$rel" = 4.0 ]
+then
+ case $arch in
+ 3[34]??|3[34]??,*)
+ if [ -d /usr/sadm/sysadm/add-ons/WIN-TCP ]
+ then
+ os=NCR.MP-RAS.2.x
+ elif [ -d /usr/sadm/sysadm/add-ons/inet ]
+ then
+ os=NCR.MP-RAS.3.x
+ fi
+ ;;
+ esac
+fi
+
+case $os
+in
+ DYNIX-ptx) os=PTX;;
+ Paragon*) os=Paragon;;
+ HP-UX) rel=`echo $rel | sed -e 's/^[^.]*\.0*//'`;;
+ AIX) rela=$rel
+ rel=`uname -v`
+ case $rel in
+ 2) arch=""
+ ;;
+ 4) if [ "$rela" = "3" ]
+ then
+ arch=$rela
+ fi
+ ;;
+ esac
+ rel=$rel.$rela
+ ;;
+ BSD-386) os=BSD-OS;;
+ SCO_SV) os=SCO; rel=`uname -X | sed -n 's/Release = 3.2v//p'`;;
+ UNIX_System_V) if [ "$arch" = "ds90" ]
+ then
+ os="UXPDS"
+ rel=`uname -v | sed -e 's/\(V.*\)L.*/\1/'`
+ fi;;
+ SINIX-?) os=SINIX;;
+ DomainOS) case $rel in
+ 10.4*) rel=10.4;;
+ esac
+ ;;
+esac
+
+# get "base part" of operating system release
+rroot=`echo $rel | sed -e 's/\.[^.]*$//'`
+rbase=`echo $rel | sed -e 's/\..*//'`
+if [ "$rroot" = "$rbase" ]
+then
+ rroot=$rel
+fi
+
+# heuristic tweaks to clean up names -- PLEASE LIMIT THESE!
+if [ "$os" = "unix" ]
+then
+ # might be Altos System V
+ case $rel
+ in
+ 5.3*) os=Altos;;
+ esac
+elif [ -r /unix -a -r /usr/lib/libseq.a -a -r /lib/cpp ]
+then
+ # might be a DYNIX/ptx 2.x system, which has a broken uname
+ if strings /lib/cpp | grep _SEQUENT_ > /dev/null
+ then
+ os=PTX
+ fi
+elif [ -d /usr/nec ]
+then
+ # NEC machine -- what is it running?
+ if [ "$os" = "UNIX_System_V" ]
+ then
+ os=EWS-UX_V
+ elif [ "$os" = "UNIX_SV" ]
+ then
+ os=UX4800
+ fi
+elif [ "$arch" = "mips" ]
+then
+ case $rel
+ in
+ 4_*)
+ if [ `uname -v` = "UMIPS" ]
+ then
+ os=RISCos
+ fi;;
+ esac
+fi
+
+# see if there is a "user suffix" specified
+if [ "${SENDMAIL_SUFFIX-}x" = "x" ]
+then
+ sfx=""
+else
+ sfx=".${SENDMAIL_SUFFIX}"
+fi
+
+echo "Configuration: os=$os, rel=$rel, rbase=$rbase, rroot=$rroot, arch=$arch, sfx=$sfx"
+
+
+SMROOT=${SMROOT-..}
+BUILDTOOLS=${BUILDTOOLS-$SMROOT/BuildTools}
+export SMROOT BUILDTOOLS
+
+# see if we are in a Build-able directory
+if [ ! -f Makefile.m4 ]; then
+ echo "Makefile.m4 not found. Build can only be run from a source directory."
+ exit $EX_UNAVAILABLE
+fi
+
+# now try to find a reasonable object directory
+if [ -r obj.$os.$rel.$arch$sfx ]; then
+ obj=obj.$os.$rel.$arch$sfx
+elif [ -r obj.$os.$rroot.$arch$sfx ]; then
+ obj=obj.$os.$rroot.$arch$sfx
+elif [ -r obj.$os.$rbase.x.$arch$sfx ]; then
+ obj=obj.$os.$rbase.x.$arch$sfx
+elif [ -r obj.$os.$rel$sfx ]; then
+ obj=obj.$os.$rel$sfx
+elif [ -r obj.$os.$rbase.x$sfx ]; then
+ obj=obj.$os.$rbase.x$sfx
+elif [ -r obj.$os.$arch$sfx ]; then
+ obj=obj.$os.$arch$sfx
+elif [ -r obj.$rel.$arch$sfx ]; then
+ obj=obj.$rel.$arch$sfx
+elif [ -r obj.$rbase.x.$arch$sfx ]; then
+ obj=obj.$rbase.x.$arch$sfx
+elif [ -r obj.$os$sfx ]; then
+ obj=obj.$os$sfx
+elif [ -r obj.$arch$sfx ]; then
+ obj=obj.$arch$sfx
+elif [ -r obj.$rel$sfx ]; then
+ obj=obj.$rel$sfx
+elif [ -r obj$sfx ]; then
+ obj=obj$sfx
+fi
+if [ -z "$obj" -o "$cflag" ]
+then
+ if [ -n "$obj" ]
+ then
+ echo "Clearing out existing $obj tree"
+ rm -rf $obj
+ else
+ # no existing obj directory -- try to create one if Makefile found
+ obj=obj.$os.$rel.$arch$sfx
+ fi
+ if [ -r $BUILDTOOLS/OS/$os.$rel.$arch$sfx ]; then
+ oscf=$os.$rel.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rel.$arch ]; then
+ oscf=$os.$rel.$arch
+ elif [ -r $BUILDTOOLS/OS/$os.$rroot.$arch$sfx ]; then
+ oscf=$os.$rroot.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rroot.$arch ]; then
+ oscf=$os.$rroot.$arch
+ elif [ -r $BUILDTOOLS/OS/$os.$rbase.x.$arch$sfx ]; then
+ oscf=$os.$rbase.x.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rbase.x.$arch ]; then
+ oscf=$os.$rbase.x.$arch
+ elif [ -r $BUILDTOOLS/OS/$os.$rel$sfx ]; then
+ oscf=$os.$rel$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rel ]; then
+ oscf=$os.$rel
+ elif [ -r $BUILDTOOLS/OS/$os.$rroot$sfx ]; then
+ oscf=$os.$rroot$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rroot ]; then
+ oscf=$os.$rroot
+ elif [ -r $BUILDTOOLS/OS/$os.$rbase.x$sfx ]; then
+ oscf=$os.$rbase.x$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$rbase.x ]; then
+ oscf=$os.$rbase.x
+ elif [ -r $BUILDTOOLS/OS/$os.$arch$sfx ]; then
+ oscf=$os.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$os.$arch ]; then
+ oscf=$os.$arch
+ elif [ -r $BUILDTOOLS/OS/$rel.$arch$sfx ]; then
+ oscf=$rel.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$rel.$arch ]; then
+ oscf=$rel.$arch
+ elif [ -r $BUILDTOOLS/OS/$rroot.$arch$sfx ]; then
+ oscf=$rroot.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$rroot.$arch ]; then
+ oscf=$rroot.$arch
+ elif [ -r $BUILDTOOLS/OS/$rbase.x.$arch$sfx ]; then
+ oscf=$rbase.x.$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$rbase.x.$arch ]; then
+ oscf=$rbase.x.$arch
+ elif [ -r $BUILDTOOLS/OS/$os$sfx ]; then
+ oscf=$os$sfx
+ elif [ -r $BUILDTOOLS/OS/$os ]; then
+ oscf=$os
+ elif [ -r $BUILDTOOLS/OS/$arch$sfx ]; then
+ oscf=$arch$sfx
+ elif [ -r $BUILDTOOLS/OS/$arch ]; then
+ oscf=$arch
+ elif [ -r $BUILDTOOLS/OS/$rel$sfx ]; then
+ oscf=$rel$sfx
+ elif [ -r $BUILDTOOLS/OS/$rel ]; then
+ oscf=$rel
+ elif [ -r $BUILDTOOLS/OS/$rel$sfx ]; then
+ oscf=$rel$sfx
+ else
+ echo "Cannot determine how to support $arch.$os.$rel" >&2
+ exit $EX_UNAVAILABLE
+ fi
+ M4=`sh $BUILDTOOLS/bin/find_m4.sh`
+ ret=$?
+ if [ $ret -ne 0 ]
+ then
+ exit $ret
+ fi
+ echo "Using M4=$M4"
+ export M4
+ if [ "$mflag" ]
+ then
+ echo "Will run in virgin $obj using $BUILDTOOLS/OS/$oscf"
+ exit 0
+ fi
+ if [ "$ABI" ]
+ then
+ echo "Using ABI $ABI"
+ fi
+ echo "Creating $obj using $BUILDTOOLS/OS/$oscf"
+ mkdir $obj
+ (cd $obj; ln -s ../*.[ch158] .)
+ if [ -f sendmail.hf ]
+ then
+ (cd $obj; ln -s ../sendmail.hf .)
+ fi
+
+ rm -f $obj/.settings$$
+ echo 'divert(-1)' > $obj/.settings$$
+ cat $BUILDTOOLS/M4/header.m4 >> $obj/.settings$$
+ if [ "$ABI" ]
+ then
+ echo "define(\`confABI', \`$ABI')" >> $obj/.settings$$
+ fi
+ cat $BUILDTOOLS/OS/$oscf >> $obj/.settings$$
+
+ if [ -z "$siteconfig" ]
+ then
+ # none specified, use defaults
+ if [ -f $BUILDTOOLS/Site/site.$oscf$sfx.m4 ]
+ then
+ siteconfig=$BUILDTOOLS/Site/site.$oscf$sfx.m4
+ elif [ -f $BUILDTOOLS/Site/site.$oscf.m4 ]
+ then
+ siteconfig=$BUILDTOOLS/Site/site.$oscf.m4
+ fi
+ if [ -f $BUILDTOOLS/Site/site.config.m4 ]
+ then
+ siteconfig="$BUILDTOOLS/Site/site.config.m4 $siteconfig"
+ fi
+ fi
+ if [ ! -z "$siteconfig" ]
+ then
+ echo "Including $siteconfig"
+ cat $siteconfig >> $obj/.settings$$
+ fi
+ if [ "$libdirs" ]
+ then
+ echo "define(\`confLIBDIRS', confLIBDIRS \`\`$libdirs'')" >> $obj/.settings$$
+ fi
+ if [ "$incdirs" ]
+ then
+ echo "define(\`confINCDIRS', confINCDIRS \`\`$incdirs'')" >> $obj/.settings$$
+ fi
+ echo 'divert(0)dnl' >> $obj/.settings$$
+ libdirs=`(cat $obj/.settings$$; echo "_SRIDBIL_= confLIBDIRS" ) | \
+ sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' | \
+ ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - | \
+ grep "^_SRIDBIL_=" | \
+ sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' -e "s/^_SRIDBIL_=//"`
+ libsrch=`(cat $obj/.settings$$; echo "_HCRSBIL_= confLIBSEARCH" ) | \
+ sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' | \
+ ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - | \
+ grep "^_HCRSBIL_=" | \
+ sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' -e "s/^_HCRSBIL_=//"`
+ echo 'divert(-1)' >> $obj/.settings$$
+ LIBDIRS="$libdirs" LIBSRCH="$libsrch" SITECONFIG="$siteconfig" sh $BUILDTOOLS/bin/configure.sh $sflag $oscf >> $obj/.settings$$
+ echo 'divert(0)dnl' >> $obj/.settings$$
+ sed -e 's/\(.\)include/\1_include_/g' -e 's/#define/#_define_/g' $obj/.settings$$ | \
+ ${M4} -DconfBUILDTOOLSDIR=$BUILDTOOLS - Makefile.m4 | \
+ sed -e 's/#_define_/#define/g' -e 's/_include_/include/g' > $obj/Makefile
+ if [ $? -ne 0 -o ! -s $obj/Makefile ]
+ then
+ echo "ERROR: ${M4} failed; You may need a newer version of M4, at least as new as System V or GNU" 1>&2
+ rm -rf $obj
+ exit $EX_UNAVAILABLE
+ fi
+ rm -f $obj/.settings$$
+ echo "Making dependencies in $obj"
+ (cd $obj; ${MAKE-make} depend)
+fi
+
+if [ "$mflag" ]
+then
+ makefile=`ls -l $obj/Makefile | sed 's/.* //'`
+ if [ -z "$makefile" ]
+ then
+ echo "ERROR: $obj exists but has no Makefile" >&2
+ exit $EX_NOINPUT
+ fi
+ echo "Will run in existing $obj using $makefile"
+ exit 0
+fi
+
+echo "Making in $obj"
+cd $obj
+eval exec ${MAKE-make} $makeargs
diff --git a/contrib/sendmail/src/map.c b/contrib/sendmail/src/map.c
new file mode 100644
index 0000000..4c95b7f
--- /dev/null
+++ b/contrib/sendmail/src/map.c
@@ -0,0 +1,5065 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)map.c 8.239 (Berkeley) 6/5/98";
+#endif /* not lint */
+
+#include "sendmail.h"
+
+#ifdef NDBM
+# include <ndbm.h>
+# ifdef R_FIRST
+ ERROR README: You are running the Berkeley DB version of ndbm.h. See
+ ERROR README: the README file about tweaking Berkeley DB so it can
+ ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile
+ ERROR README: and use -DNEWDB instead.
+# endif
+#endif
+#ifdef NEWDB
+# include <db.h>
+# ifndef DB_VERSION_MAJOR
+# define DB_VERSION_MAJOR 1
+# endif
+#endif
+#ifdef NIS
+ struct dom_binding; /* forward reference needed on IRIX */
+# include <rpcsvc/ypclnt.h>
+# ifdef NDBM
+# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
+# endif
+#endif
+
+/*
+** MAP.C -- implementations for various map classes.
+**
+** Each map class implements a series of functions:
+**
+** bool map_parse(MAP *map, char *args)
+** Parse the arguments from the config file. Return TRUE
+** if they were ok, FALSE otherwise. Fill in map with the
+** values.
+**
+** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
+** Look up the key in the given map. If found, do any
+** rewriting the map wants (including "args" if desired)
+** and return the value. Set *pstat to the appropriate status
+** on error and return NULL. Args will be NULL if called
+** from the alias routines, although this should probably
+** not be relied upon. It is suggested you call map_rewrite
+** to return the results -- it takes care of null termination
+** and uses a dynamically expanded buffer as needed.
+**
+** void map_store(MAP *map, char *key, char *value)
+** Store the key:value pair in the map.
+**
+** bool map_open(MAP *map, int mode)
+** Open the map for the indicated mode. Mode should
+** be either O_RDONLY or O_RDWR. Return TRUE if it
+** was opened successfully, FALSE otherwise. If the open
+** failed an the MF_OPTIONAL flag is not set, it should
+** also print an error. If the MF_ALIAS bit is set
+** and this map class understands the @:@ convention, it
+** should call aliaswait() before returning.
+**
+** void map_close(MAP *map)
+** Close the map.
+**
+** This file also includes the implementation for getcanonname.
+** It is currently implemented in a pretty ad-hoc manner; it ought
+** to be more properly integrated into the map structure.
+*/
+
+#define DBMMODE 0644
+
+#ifndef EX_NOTFOUND
+# define EX_NOTFOUND EX_NOHOST
+#endif
+
+extern bool aliaswait __P((MAP *, char *, int));
+extern bool extract_canonname __P((char *, char *, char[], int));
+
+#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
+# define LOCK_ON_OPEN 1 /* we can open/create a locked file */
+#else
+# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
+#endif
+
+#ifndef O_ACCMODE
+# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
+#endif
+ /*
+** MAP_PARSEARGS -- parse config line arguments for database lookup
+**
+** This is a generic version of the map_parse method.
+**
+** Parameters:
+** map -- the map being initialized.
+** ap -- a pointer to the args on the config line.
+**
+** Returns:
+** TRUE -- if everything parsed OK.
+** FALSE -- otherwise.
+**
+** Side Effects:
+** null terminates the filename; stores it in map
+*/
+
+bool
+map_parseargs(map, ap)
+ MAP *map;
+ char *ap;
+{
+ register char *p = ap;
+
+ map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
+ for (;;)
+ {
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p != '-')
+ break;
+ switch (*++p)
+ {
+ case 'N':
+ map->map_mflags |= MF_INCLNULL;
+ map->map_mflags &= ~MF_TRY0NULL;
+ break;
+
+ case 'O':
+ map->map_mflags &= ~MF_TRY1NULL;
+ break;
+
+ case 'o':
+ map->map_mflags |= MF_OPTIONAL;
+ break;
+
+ case 'f':
+ map->map_mflags |= MF_NOFOLDCASE;
+ break;
+
+ case 'm':
+ map->map_mflags |= MF_MATCHONLY;
+ break;
+
+ case 'A':
+ map->map_mflags |= MF_APPEND;
+ break;
+
+ case 'q':
+ map->map_mflags |= MF_KEEPQUOTES;
+ break;
+
+ case 'a':
+ map->map_app = ++p;
+ break;
+
+ case 'T':
+ map->map_tapp = ++p;
+ break;
+
+ case 'k':
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ map->map_keycolnm = p;
+ break;
+
+ case 'v':
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ map->map_valcolnm = p;
+ break;
+
+ case 'z':
+ if (*++p != '\\')
+ map->map_coldelim = *p;
+ else
+ {
+ switch (*++p)
+ {
+ case 'n':
+ map->map_coldelim = '\n';
+ break;
+
+ case 't':
+ map->map_coldelim = '\t';
+ break;
+
+ default:
+ map->map_coldelim = '\\';
+ }
+ }
+ break;
+
+ case 't':
+ map->map_mflags |= MF_NODEFER;
+ break;
+
+#ifdef RESERVED_FOR_SUN
+ case 'd':
+ map->map_mflags |= MF_DOMAIN_WIDE;
+ break;
+
+ case 's':
+ /* info type */
+ break;
+#endif
+ }
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+ if (map->map_app != NULL)
+ map->map_app = newstr(map->map_app);
+ if (map->map_tapp != NULL)
+ map->map_tapp = newstr(map->map_tapp);
+ if (map->map_keycolnm != NULL)
+ map->map_keycolnm = newstr(map->map_keycolnm);
+ if (map->map_valcolnm != NULL)
+ map->map_valcolnm = newstr(map->map_valcolnm);
+
+ if (*p != '\0')
+ {
+ map->map_file = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ map->map_file = newstr(map->map_file);
+ }
+
+ while (*p != '\0' && isascii(*p) && isspace(*p))
+ p++;
+ if (*p != '\0')
+ map->map_rebuild = newstr(p);
+
+ if (map->map_file == NULL &&
+ !bitset(MCF_OPTFILE, map->map_class->map_cflags))
+ {
+ syserr("No file name for %s map %s",
+ map->map_class->map_cname, map->map_mname);
+ return FALSE;
+ }
+ return TRUE;
+}
+ /*
+** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
+**
+** It also adds the map_app string. It can be used as a utility
+** in the map_lookup method.
+**
+** Parameters:
+** map -- the map that causes this.
+** s -- the string to rewrite, NOT necessarily null terminated.
+** slen -- the length of s.
+** av -- arguments to interpolate into buf.
+**
+** Returns:
+** Pointer to rewritten result. This is static data that
+** should be copied if it is to be saved!
+**
+** Side Effects:
+** none.
+*/
+
+char *
+map_rewrite(map, s, slen, av)
+ register MAP *map;
+ register const char *s;
+ size_t slen;
+ char **av;
+{
+ register char *bp;
+ register char c;
+ char **avp;
+ register char *ap;
+ size_t l;
+ size_t len;
+ static size_t buflen = 0;
+ static char *buf = NULL;
+
+ if (tTd(39, 1))
+ {
+ printf("map_rewrite(%.*s), av =", (int)slen, s);
+ if (av == NULL)
+ printf(" (nullv)");
+ else
+ {
+ for (avp = av; *avp != NULL; avp++)
+ printf("\n\t%s", *avp);
+ }
+ printf("\n");
+ }
+
+ /* count expected size of output (can safely overestimate) */
+ l = len = slen;
+ if (av != NULL)
+ {
+ const char *sp = s;
+
+ while (l-- > 0 && (c = *sp++) != '\0')
+ {
+ if (c != '%')
+ continue;
+ if (l-- <= 0)
+ break;
+ c = *sp++;
+ if (!(isascii(c) && isdigit(c)))
+ continue;
+ for (avp = av; --c >= '0' && *avp != NULL; avp++)
+ continue;
+ if (*avp == NULL)
+ continue;
+ len += strlen(*avp);
+ }
+ }
+ if (map->map_app != NULL)
+ len += strlen(map->map_app);
+ if (buflen < ++len)
+ {
+ /* need to malloc additional space */
+ buflen = len;
+ if (buf != NULL)
+ free(buf);
+ buf = xalloc(buflen);
+ }
+
+ bp = buf;
+ if (av == NULL)
+ {
+ bcopy(s, bp, slen);
+ bp += slen;
+ }
+ else
+ {
+ while (slen-- > 0 && (c = *s++) != '\0')
+ {
+ if (c != '%')
+ {
+ pushc:
+ *bp++ = c;
+ continue;
+ }
+ if (slen-- <= 0 || (c = *s++) == '\0')
+ c = '%';
+ if (c == '%')
+ goto pushc;
+ if (!(isascii(c) && isdigit(c)))
+ {
+ *bp++ = '%';
+ goto pushc;
+ }
+ for (avp = av; --c >= '0' && *avp != NULL; avp++)
+ continue;
+ if (*avp == NULL)
+ continue;
+
+ /* transliterate argument into output string */
+ for (ap = *avp; (c = *ap++) != '\0'; )
+ *bp++ = c;
+ }
+ }
+ if (map->map_app != NULL)
+ strcpy(bp, map->map_app);
+ else
+ *bp = '\0';
+ if (tTd(39, 1))
+ printf("map_rewrite => %s\n", buf);
+ return buf;
+}
+ /*
+** INITMAPS -- initialize for aliasing
+**
+** Parameters:
+** rebuild -- if TRUE, this rebuilds the cached versions.
+** e -- current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** initializes aliases:
+** if alias database: opens the database.
+** if no database available: reads aliases into the symbol table.
+*/
+
+void
+initmaps(rebuild, e)
+ bool rebuild;
+ register ENVELOPE *e;
+{
+ extern void map_init __P((STAB *, int));
+
+#if XDEBUG
+ checkfd012("entering initmaps");
+#endif
+ CurEnv = e;
+
+ stabapply(map_init, 0);
+ stabapply(map_init, rebuild ? 2 : 1);
+#if XDEBUG
+ checkfd012("exiting initmaps");
+#endif
+}
+
+void
+map_init(s, pass)
+ register STAB *s;
+ int pass;
+{
+ bool rebuildable;
+ register MAP *map;
+
+ /* has to be a map */
+ if (s->s_type != ST_MAP)
+ return;
+
+ map = &s->s_map;
+ if (!bitset(MF_VALID, map->map_mflags))
+ return;
+
+ if (tTd(38, 2))
+ printf("map_init(%s:%s, %s, %d)\n",
+ map->map_class->map_cname == NULL ? "NULL" :
+ map->map_class->map_cname,
+ map->map_mname == NULL ? "NULL" : map->map_mname,
+ map->map_file == NULL ? "NULL" : map->map_file,
+ pass);
+
+ /*
+ ** Pass 0 opens all non-rebuildable maps.
+ ** Pass 1 opens all rebuildable maps for read.
+ ** Pass 2 rebuilds all rebuildable maps.
+ */
+
+ rebuildable = (bitset(MF_ALIAS, map->map_mflags) &&
+ bitset(MCF_REBUILDABLE, map->map_class->map_cflags));
+
+ if ((pass == 0 && rebuildable) ||
+ ((pass == 1 || pass == 2) && !rebuildable))
+ {
+ if (tTd(38, 3))
+ printf("\twrong pass (pass = %d, rebuildable = %d)\n",
+ pass, rebuildable);
+ return;
+ }
+
+ /* if already open, close it (for nested open) */
+ if (bitset(MF_OPEN, map->map_mflags))
+ {
+ map->map_class->map_close(map);
+ map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+ }
+
+ if (pass == 2)
+ {
+ (void) rebuildaliases(map, FALSE);
+ return;
+ }
+
+ if (map->map_class->map_open(map, O_RDONLY))
+ {
+ if (tTd(38, 4))
+ printf("\t%s:%s %s: valid\n",
+ map->map_class->map_cname == NULL ? "NULL" :
+ map->map_class->map_cname,
+ map->map_mname == NULL ? "NULL" :
+ map->map_mname,
+ map->map_file == NULL ? "NULL" :
+ map->map_file);
+ map->map_mflags |= MF_OPEN;
+ }
+ else
+ {
+ if (tTd(38, 4))
+ printf("\t%s:%s %s: invalid: %s\n",
+ map->map_class->map_cname == NULL ? "NULL" :
+ map->map_class->map_cname,
+ map->map_mname == NULL ? "NULL" :
+ map->map_mname,
+ map->map_file == NULL ? "NULL" :
+ map->map_file,
+ errstring(errno));
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ extern MAPCLASS BogusMapClass;
+
+ map->map_class = &BogusMapClass;
+ map->map_mflags |= MF_OPEN;
+ }
+ }
+}
+ /*
+** GETCANONNAME -- look up name using service switch
+**
+** Parameters:
+** host -- the host name to look up.
+** hbsize -- the size of the host buffer.
+** trymx -- if set, try MX records.
+**
+** Returns:
+** TRUE -- if the host was found.
+** FALSE -- otherwise.
+*/
+
+bool
+getcanonname(host, hbsize, trymx)
+ char *host;
+ int hbsize;
+ bool trymx;
+{
+ int nmaps;
+ int mapno;
+ bool found = FALSE;
+ bool got_tempfail = FALSE;
+ auto int stat;
+ char *maptype[MAXMAPSTACK];
+ short mapreturn[MAXMAPACTIONS];
+
+ nmaps = switch_map_find("hosts", maptype, mapreturn);
+ for (mapno = 0; mapno < nmaps; mapno++)
+ {
+ int i;
+
+ if (tTd(38, 20))
+ printf("getcanonname(%s), trying %s\n",
+ host, maptype[mapno]);
+ if (strcmp("files", maptype[mapno]) == 0)
+ {
+ extern bool text_getcanonname __P((char *, int, int *));
+
+ found = text_getcanonname(host, hbsize, &stat);
+ }
+#ifdef NIS
+ else if (strcmp("nis", maptype[mapno]) == 0)
+ {
+ extern bool nis_getcanonname __P((char *, int, int *));
+
+ found = nis_getcanonname(host, hbsize, &stat);
+ }
+#endif
+#ifdef NISPLUS
+ else if (strcmp("nisplus", maptype[mapno]) == 0)
+ {
+ extern bool nisplus_getcanonname __P((char *, int, int *));
+
+ found = nisplus_getcanonname(host, hbsize, &stat);
+ }
+#endif
+#if NAMED_BIND
+ else if (strcmp("dns", maptype[mapno]) == 0)
+ {
+ extern bool dns_getcanonname __P((char *, int, bool, int *));
+
+ found = dns_getcanonname(host, hbsize, trymx, &stat);
+ }
+#endif
+#if NETINFO
+ else if (strcmp("netinfo", maptype[mapno]) == 0)
+ {
+ extern bool ni_getcanonname __P((char *, int, int *));
+
+ found = ni_getcanonname(host, hbsize, &stat);
+ }
+#endif
+ else
+ {
+ found = FALSE;
+ stat = EX_UNAVAILABLE;
+ }
+
+ /*
+ ** Heuristic: if $m is not set, we are running during system
+ ** startup. In this case, when a name is apparently found
+ ** but has no dot, treat is as not found. This avoids
+ ** problems if /etc/hosts has no FQDN but is listed first
+ ** in the service switch.
+ */
+
+ if (found &&
+ (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL))
+ break;
+
+ /* see if we should continue */
+ if (stat == EX_TEMPFAIL)
+ {
+ i = MA_TRYAGAIN;
+ got_tempfail = TRUE;
+ }
+ else if (stat == EX_NOTFOUND)
+ i = MA_NOTFOUND;
+ else
+ i = MA_UNAVAIL;
+ if (bitset(1 << mapno, mapreturn[i]))
+ break;
+ }
+
+ if (found)
+ {
+ char *d;
+
+ if (tTd(38, 20))
+ printf("getcanonname(%s), found\n", host);
+
+ /*
+ ** If returned name is still single token, compensate
+ ** by tagging on $m. This is because some sites set
+ ** up their DNS or NIS databases wrong.
+ */
+
+ if ((d = strchr(host, '.')) == NULL || d[1] == '\0')
+ {
+ d = macvalue('m', CurEnv);
+ if (d != NULL &&
+ hbsize > (int) (strlen(host) + strlen(d) + 1))
+ {
+ if (host[strlen(host) - 1] != '.')
+ strcat(host, ".");
+ strcat(host, d);
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+
+ if (tTd(38, 20))
+ printf("getcanonname(%s), failed, stat=%d\n", host, stat);
+
+#if NAMED_BIND
+ if (got_tempfail)
+ h_errno = TRY_AGAIN;
+ else
+ h_errno = HOST_NOT_FOUND;
+#endif
+
+ return FALSE;
+}
+ /*
+** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
+**
+** Parameters:
+** name -- the name against which to match.
+** line -- the /etc/hosts line.
+** cbuf -- the location to store the result.
+** cbuflen -- the size of cbuf.
+**
+** Returns:
+** TRUE -- if the line matched the desired name.
+** FALSE -- otherwise.
+*/
+
+bool
+extract_canonname(name, line, cbuf, cbuflen)
+ char *name;
+ char *line;
+ char cbuf[];
+ int cbuflen;
+{
+ int i;
+ char *p;
+ bool found = FALSE;
+ extern char *get_column __P((char *, int, char, char *, int));
+
+ cbuf[0] = '\0';
+ if (line[0] == '#')
+ return FALSE;
+
+ for (i = 1; ; i++)
+ {
+ char nbuf[MAXNAME + 1];
+
+ p = get_column(line, i, '\0', nbuf, sizeof nbuf);
+ if (p == NULL)
+ break;
+ if (*p == '\0')
+ continue;
+ if (cbuf[0] == '\0' ||
+ (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
+ {
+ snprintf(cbuf, cbuflen, "%s", p);
+ }
+ if (strcasecmp(name, p) == 0)
+ found = TRUE;
+ }
+ if (found && strchr(cbuf, '.') == NULL)
+ {
+ /* try to add a domain on the end of the name */
+ char *domain = macvalue('m', CurEnv);
+
+ if (domain != NULL &&
+ strlen(domain) + strlen(cbuf) + 1 < cbuflen)
+ {
+ p = &cbuf[strlen(cbuf)];
+ *p++ = '.';
+ strcpy(p, domain);
+ }
+ }
+ return found;
+}
+ /*
+** NDBM modules
+*/
+
+#ifdef NDBM
+
+/*
+** NDBM_MAP_OPEN -- DBM-style map open
+*/
+
+bool
+ndbm_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ register DBM *dbm;
+ struct stat st;
+ int dfd;
+ int pfd;
+ int sff;
+ int ret;
+ int smode = S_IREAD;
+ char dirfile[MAXNAME + 1];
+ char pagfile[MAXNAME + 1];
+ struct stat std, stp;
+
+ if (tTd(38, 2))
+ printf("ndbm_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+ map->map_lockfd = -1;
+ mode &= O_ACCMODE;
+
+ /* do initial file and directory checks */
+ snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file);
+ snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file);
+ sff = SFF_ROOTOK|SFF_REGONLY;
+ if (mode == O_RDWR)
+ {
+ sff |= SFF_CREAT;
+ if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
+ sff |= SFF_NOSLINK;
+ if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
+ sff |= SFF_NOHLINK;
+ smode = S_IWRITE;
+ }
+ else
+ {
+ if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
+ sff |= SFF_NOWLINK;
+ }
+ if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
+ sff |= SFF_SAFEDIRPATH;
+ ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName,
+ sff, smode, &std);
+ if (ret == 0)
+ ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName,
+ sff, smode, &stp);
+ if (ret == ENOENT && AutoRebuild &&
+ bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
+ (bitset(MF_IMPL_NDBM, map->map_mflags) ||
+ bitset(MF_ALIAS, map->map_mflags)) &&
+ mode == O_RDONLY)
+ {
+ bool impl = bitset(MF_IMPL_NDBM, map->map_mflags);
+ extern bool impl_map_open __P((MAP *, int));
+
+ /* may be able to rebuild */
+ map->map_mflags &= ~MF_IMPL_NDBM;
+ if (!rebuildaliases(map, TRUE))
+ return FALSE;
+ if (impl)
+ return impl_map_open(map, O_RDONLY);
+ else
+ return ndbm_map_open(map, O_RDONLY);
+ }
+ if (ret != 0)
+ {
+ char *prob = "unsafe";
+
+ /* cannot open this map */
+ if (ret == ENOENT)
+ prob = "missing";
+ if (tTd(38, 2))
+ printf("\t%s map file: %d\n", prob, ret);
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("dbm map \"%s\": %s map file %s",
+ map->map_mname, prob, map->map_file);
+ return FALSE;
+ }
+ if (std.st_mode == ST_MODE_NOFILE)
+ mode |= O_CREAT|O_EXCL;
+
+#if LOCK_ON_OPEN
+ if (mode == O_RDONLY)
+ mode |= O_SHLOCK;
+ else
+ mode |= O_TRUNC|O_EXLOCK;
+#else
+ if ((mode & O_ACCMODE) == O_RDWR)
+ {
+# if NOFTRUNCATE
+ /*
+ ** Warning: race condition. Try to lock the file as
+ ** quickly as possible after opening it.
+ ** This may also have security problems on some systems,
+ ** but there isn't anything we can do about it.
+ */
+
+ mode |= O_TRUNC;
+# else
+ /*
+ ** This ugly code opens the map without truncating it,
+ ** locks the file, then truncates it. Necessary to
+ ** avoid race conditions.
+ */
+
+ int dirfd;
+ int pagfd;
+ int sff = SFF_CREAT|SFF_OPENASROOT;
+
+ if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
+ sff |= SFF_NOSLINK;
+ if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
+ sff |= SFF_NOHLINK;
+
+ dirfd = safeopen(dirfile, mode, DBMMODE, sff);
+ pagfd = safeopen(pagfile, mode, DBMMODE, sff);
+
+ if (dirfd < 0 || pagfd < 0)
+ {
+ int save_errno = errno;
+
+ if (dirfd >= 0)
+ (void) close(dirfd);
+ if (pagfd >= 0)
+ (void) close(pagfd);
+ errno = save_errno;
+ syserr("ndbm_map_open: cannot create database %s",
+ map->map_file);
+ return FALSE;
+ }
+ if (ftruncate(dirfd, (off_t) 0) < 0 ||
+ ftruncate(pagfd, (off_t) 0) < 0)
+ {
+ int save_errno = errno;
+
+ (void) close(dirfd);
+ (void) close(pagfd);
+ errno = save_errno;
+ syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
+ map->map_file);
+ return FALSE;
+ }
+
+ /* if new file, get "before" bits for later filechanged check */
+ if (std.st_mode == ST_MODE_NOFILE &&
+ (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0))
+ {
+ int save_errno = errno;
+
+ (void) close(dirfd);
+ (void) close(pagfd);
+ errno = save_errno;
+ syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
+ map->map_file);
+ return FALSE;
+ }
+
+ /* have to save the lock for the duration (bletch) */
+ map->map_lockfd = dirfd;
+ close(pagfd);
+
+ /* twiddle bits for dbm_open */
+ mode &= ~(O_CREAT|O_EXCL);
+# endif
+ }
+#endif
+
+ /* open the database */
+ dbm = dbm_open(map->map_file, mode, DBMMODE);
+ if (dbm == NULL)
+ {
+ int save_errno = errno;
+
+ if (bitset(MF_ALIAS, map->map_mflags) &&
+ aliaswait(map, ".pag", FALSE))
+ return TRUE;
+#if !LOCK_ON_OPEN && !NOFTRUNCATE
+ if (map->map_lockfd >= 0)
+ close(map->map_lockfd);
+#endif
+ errno = save_errno;
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("Cannot open DBM database %s", map->map_file);
+ return FALSE;
+ }
+ dfd = dbm_dirfno(dbm);
+ pfd = dbm_pagfno(dbm);
+ if (dfd == pfd)
+ {
+ /* heuristic: if files are linked, this is actually gdbm */
+ dbm_close(dbm);
+#if !LOCK_ON_OPEN && !NOFTRUNCATE
+ if (map->map_lockfd >= 0)
+ close(map->map_lockfd);
+#endif
+ errno = 0;
+ syserr("dbm map \"%s\": cannot support GDBM",
+ map->map_mname);
+ return FALSE;
+ }
+
+ if (filechanged(dirfile, dfd, &std) ||
+ filechanged(pagfile, pfd, &stp))
+ {
+ int save_errno = errno;
+
+ dbm_close(dbm);
+#if !LOCK_ON_OPEN && !NOFTRUNCATE
+ if (map->map_lockfd >= 0)
+ close(map->map_lockfd);
+#endif
+ errno = save_errno;
+ syserr("ndbm_map_open(%s): file changed after open",
+ map->map_file);
+ return FALSE;
+ }
+
+ map->map_db1 = (ARBPTR_T) dbm;
+ if (mode == O_RDONLY)
+ {
+#if LOCK_ON_OPEN
+ if (dfd >= 0)
+ (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN);
+ if (pfd >= 0)
+ (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN);
+#endif
+ if (bitset(MF_ALIAS, map->map_mflags) &&
+ !aliaswait(map, ".pag", TRUE))
+ return FALSE;
+ }
+ else
+ {
+ map->map_mflags |= MF_LOCKED;
+ if (geteuid() == 0 && TrustedFileUid != 0)
+ {
+ if (fchown(dfd, TrustedFileUid, -1) < 0 ||
+ fchown(pfd, TrustedFileUid, -1) < 0)
+ {
+ int err = errno;
+
+ sm_syslog(LOG_ALERT, NOQID,
+ "ownership change on %s failed: %s",
+ map->map_file, errstring(err));
+ message("050 ownership change on %s failed: %s",
+ map->map_file, errstring(err));
+ }
+ }
+ }
+ if (fstat(dfd, &st) >= 0)
+ map->map_mtime = st.st_mtime;
+ return TRUE;
+}
+
+
+/*
+** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
+*/
+
+char *
+ndbm_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ datum key, val;
+ int fd;
+ char keybuf[MAXNAME + 1];
+ struct stat stbuf;
+
+ if (tTd(38, 20))
+ printf("ndbm_map_lookup(%s, %s)\n",
+ map->map_mname, name);
+
+ key.dptr = name;
+ key.dsize = strlen(name);
+ if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+ {
+ if (key.dsize > sizeof keybuf - 1)
+ key.dsize = sizeof keybuf - 1;
+ bcopy(key.dptr, keybuf, key.dsize);
+ keybuf[key.dsize] = '\0';
+ makelower(keybuf);
+ key.dptr = keybuf;
+ }
+lockdbm:
+ fd = dbm_dirfno((DBM *) map->map_db1);
+ if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+ (void) lockfile(fd, map->map_file, ".dir", LOCK_SH);
+ if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
+ {
+ /* Reopen the database to sync the cache */
+ int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
+ : O_RDONLY;
+
+ map->map_class->map_close(map);
+ map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+ if (map->map_class->map_open(map, omode))
+ {
+ map->map_mflags |= MF_OPEN;
+ if ((omode && O_ACCMODE) == O_RDWR)
+ map->map_mflags |= MF_WRITABLE;
+ goto lockdbm;
+ }
+ else
+ {
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ extern MAPCLASS BogusMapClass;
+
+ *statp = EX_TEMPFAIL;
+ map->map_class = &BogusMapClass;
+ map->map_mflags |= MF_OPEN;
+ syserr("Cannot reopen NDBM database %s",
+ map->map_file);
+ }
+ return NULL;
+ }
+ }
+ val.dptr = NULL;
+ if (bitset(MF_TRY0NULL, map->map_mflags))
+ {
+ val = dbm_fetch((DBM *) map->map_db1, key);
+ if (val.dptr != NULL)
+ map->map_mflags &= ~MF_TRY1NULL;
+ }
+ if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags))
+ {
+ key.dsize++;
+ val = dbm_fetch((DBM *) map->map_db1, key);
+ if (val.dptr != NULL)
+ map->map_mflags &= ~MF_TRY0NULL;
+ }
+ if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+ (void) lockfile(fd, map->map_file, ".dir", LOCK_UN);
+ if (val.dptr == NULL)
+ return NULL;
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ return map_rewrite(map, name, strlen(name), NULL);
+ else
+ return map_rewrite(map, val.dptr, val.dsize, av);
+}
+
+
+/*
+** NDBM_MAP_STORE -- store a datum in the database
+*/
+
+void
+ndbm_map_store(map, lhs, rhs)
+ register MAP *map;
+ char *lhs;
+ char *rhs;
+{
+ datum key;
+ datum data;
+ int stat;
+ char keybuf[MAXNAME + 1];
+
+ if (tTd(38, 12))
+ printf("ndbm_map_store(%s, %s, %s)\n",
+ map->map_mname, lhs, rhs);
+
+ key.dsize = strlen(lhs);
+ key.dptr = lhs;
+ if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+ {
+ if (key.dsize > sizeof keybuf - 1)
+ key.dsize = sizeof keybuf - 1;
+ bcopy(key.dptr, keybuf, key.dsize);
+ keybuf[key.dsize] = '\0';
+ makelower(keybuf);
+ key.dptr = keybuf;
+ }
+
+ data.dsize = strlen(rhs);
+ data.dptr = rhs;
+
+ if (bitset(MF_INCLNULL, map->map_mflags))
+ {
+ key.dsize++;
+ data.dsize++;
+ }
+
+ stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
+ if (stat > 0)
+ {
+ if (!bitset(MF_APPEND, map->map_mflags))
+ message("050 Warning: duplicate alias name %s", lhs);
+ else
+ {
+ static char *buf = NULL;
+ static int bufsiz = 0;
+ auto int xstat;
+ datum old;
+
+ old.dptr = ndbm_map_lookup(map, key.dptr,
+ (char **)NULL, &xstat);
+ if (old.dptr != NULL && *(char *) old.dptr != '\0')
+ {
+ old.dsize = strlen(old.dptr);
+ if (data.dsize + old.dsize + 2 > bufsiz)
+ {
+ if (buf != NULL)
+ (void) free(buf);
+ bufsiz = data.dsize + old.dsize + 2;
+ buf = xalloc(bufsiz);
+ }
+ snprintf(buf, bufsiz, "%s,%s",
+ data.dptr, old.dptr);
+ data.dsize = data.dsize + old.dsize + 1;
+ data.dptr = buf;
+ if (tTd(38, 9))
+ printf("ndbm_map_store append=%s\n", data.dptr);
+ }
+ }
+ stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE);
+ }
+ if (stat != 0)
+ syserr("readaliases: dbm put (%s)", lhs);
+}
+
+
+/*
+** NDBM_MAP_CLOSE -- close the database
+*/
+
+void
+ndbm_map_close(map)
+ register MAP *map;
+{
+ if (tTd(38, 9))
+ printf("ndbm_map_close(%s, %s, %lx)\n",
+ map->map_mname, map->map_file, map->map_mflags);
+
+ if (bitset(MF_WRITABLE, map->map_mflags))
+ {
+#ifdef NDBM_YP_COMPAT
+ bool inclnull;
+ char buf[200];
+
+ inclnull = bitset(MF_INCLNULL, map->map_mflags);
+ map->map_mflags &= ~MF_INCLNULL;
+
+ if (strstr(map->map_file, "/yp/") != NULL)
+ {
+ long save_mflags = map->map_mflags;
+
+ map->map_mflags |= MF_NOFOLDCASE;
+
+ (void) snprintf(buf, sizeof buf, "%010ld", curtime());
+ ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
+
+ (void) gethostname(buf, sizeof buf);
+ ndbm_map_store(map, "YP_MASTER_NAME", buf);
+
+ map->map_mflags = save_mflags;
+ }
+
+ if (inclnull)
+ map->map_mflags |= MF_INCLNULL;
+#endif
+
+ /* write out the distinguished alias */
+ ndbm_map_store(map, "@", "@");
+ }
+ dbm_close((DBM *) map->map_db1);
+
+ /* release lock (if needed) */
+#if !LOCK_ON_OPEN
+ if (map->map_lockfd >= 0)
+ (void) close(map->map_lockfd);
+#endif
+}
+
+#endif
+ /*
+** NEWDB (Hash and BTree) Modules
+*/
+
+#ifdef NEWDB
+
+/*
+** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
+**
+** These do rather bizarre locking. If you can lock on open,
+** do that to avoid the condition of opening a database that
+** is being rebuilt. If you don't, we'll try to fake it, but
+** there will be a race condition. If opening for read-only,
+** we immediately release the lock to avoid freezing things up.
+** We really ought to hold the lock, but guarantee that we won't
+** be pokey about it. That's hard to do.
+*/
+
+#if DB_VERSION_MAJOR < 2
+extern bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *));
+#else
+extern bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *));
+#endif
+
+/* these should be K line arguments */
+#if DB_VERSION_MAJOR < 2
+# define db_cachesize cachesize
+# define h_nelem nelem
+# ifndef DB_CACHE_SIZE
+# define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
+# endif
+# ifndef DB_HASH_NELEM
+# define DB_HASH_NELEM 4096 /* (starting) size of hash table */
+# endif
+#endif
+
+bool
+bt_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+#if DB_VERSION_MAJOR < 2
+ BTREEINFO btinfo;
+#else
+ DB_INFO btinfo;
+#endif
+
+ if (tTd(38, 2))
+ printf("bt_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+
+ bzero(&btinfo, sizeof btinfo);
+#ifdef DB_CACHE_SIZE
+ btinfo.db_cachesize = DB_CACHE_SIZE;
+#endif
+ return db_map_open(map, mode, "btree", DB_BTREE, &btinfo);
+}
+
+bool
+hash_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+#if DB_VERSION_MAJOR < 2
+ HASHINFO hinfo;
+#else
+ DB_INFO hinfo;
+#endif
+
+ if (tTd(38, 2))
+ printf("hash_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+
+ bzero(&hinfo, sizeof hinfo);
+#ifdef DB_HASH_NELEM
+ hinfo.h_nelem = DB_HASH_NELEM;
+#endif
+#ifdef DB_CACHE_SIZE
+ hinfo.db_cachesize = DB_CACHE_SIZE;
+#endif
+ return db_map_open(map, mode, "hash", DB_HASH, &hinfo);
+}
+
+bool
+db_map_open(map, mode, mapclassname, dbtype, openinfo)
+ MAP *map;
+ int mode;
+ char *mapclassname;
+ DBTYPE dbtype;
+#if DB_VERSION_MAJOR < 2
+ const void *openinfo;
+#else
+ DB_INFO *openinfo;
+#endif
+{
+ DB *db = NULL;
+ int i;
+ int omode;
+ int smode = S_IREAD;
+ int fd;
+ int sff;
+ int saveerrno;
+ struct stat st;
+ char buf[MAXNAME + 1];
+
+ /* do initial file and directory checks */
+ snprintf(buf, sizeof buf - 3, "%s", map->map_file);
+ i = strlen(buf);
+ if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
+ (void) strcat(buf, ".db");
+
+ mode &= O_ACCMODE;
+ omode = mode;
+
+ sff = SFF_ROOTOK|SFF_REGONLY;
+ if (mode == O_RDWR)
+ {
+ sff |= SFF_CREAT;
+ if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
+ sff |= SFF_NOSLINK;
+ if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
+ sff |= SFF_NOHLINK;
+ smode = S_IWRITE;
+ }
+ else
+ {
+ if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
+ sff |= SFF_NOWLINK;
+ }
+ if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
+ sff |= SFF_SAFEDIRPATH;
+ i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st);
+ if (i == ENOENT && AutoRebuild &&
+ bitset(MCF_REBUILDABLE, map->map_class->map_cflags) &&
+ (bitset(MF_IMPL_HASH, map->map_mflags) ||
+ bitset(MF_ALIAS, map->map_mflags)) &&
+ mode == O_RDONLY)
+ {
+ bool impl = bitset(MF_IMPL_HASH, map->map_mflags);
+ extern bool impl_map_open __P((MAP *, int));
+
+ /* may be able to rebuild */
+ map->map_mflags &= ~MF_IMPL_HASH;
+ if (!rebuildaliases(map, TRUE))
+ return FALSE;
+ if (impl)
+ return impl_map_open(map, O_RDONLY);
+ else
+ return db_map_open(map, O_RDONLY, mapclassname,
+ dbtype, openinfo);
+ }
+
+ if (i != 0)
+ {
+ char *prob = "unsafe";
+
+ /* cannot open this map */
+ if (i == ENOENT)
+ prob = "missing";
+ if (tTd(38, 2))
+ printf("\t%s map file: %s\n", prob, errstring(i));
+ errno = i;
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("%s map \"%s\": %s map file %s",
+ mapclassname, map->map_mname, prob, buf);
+ return FALSE;
+ }
+ if (st.st_mode == ST_MODE_NOFILE)
+ omode |= O_CREAT|O_EXCL;
+
+ map->map_lockfd = -1;
+
+#if LOCK_ON_OPEN
+ if (mode == O_RDWR)
+ omode |= O_TRUNC|O_EXLOCK;
+ else
+ omode |= O_SHLOCK;
+#else
+ /*
+ ** Pre-lock the file to avoid race conditions. In particular,
+ ** since dbopen returns NULL if the file is zero length, we
+ ** must have a locked instance around the dbopen.
+ */
+
+ fd = open(buf, omode, DBMMODE);
+ if (fd < 0)
+ {
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("db_map_open: cannot pre-open database %s", buf);
+ return FALSE;
+ }
+
+ /* make sure no baddies slipped in just before the open... */
+ if (filechanged(buf, fd, &st))
+ {
+ int save_errno = errno;
+
+ (void) close(fd);
+ errno = save_errno;
+ syserr("db_map_open(%s): file changed after pre-open", buf);
+ return FALSE;
+ }
+
+ /* if new file, get the "before" bits for later filechanged check */
+ if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0)
+ {
+ int save_errno = errno;
+
+ (void) close(fd);
+ errno = save_errno;
+ syserr("db_map_open(%s): cannot fstat pre-opened file",
+ buf);
+ return FALSE;
+ }
+
+ /* actually lock the pre-opened file */
+ if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX))
+ syserr("db_map_open: cannot lock %s", buf);
+
+ /* set up mode bits for dbopen */
+ if (mode == O_RDWR)
+ omode |= O_TRUNC;
+ omode &= ~(O_EXCL|O_CREAT);
+#endif
+
+#if DB_VERSION_MAJOR < 2
+ db = dbopen(buf, omode, DBMMODE, dbtype, openinfo);
+#else
+ {
+ int flags = 0;
+
+ if (mode == O_RDONLY)
+ flags |= DB_RDONLY;
+ if (bitset(O_CREAT, omode))
+ flags |= DB_CREATE;
+ if (bitset(O_TRUNC, omode))
+ flags |= DB_TRUNCATE;
+
+ errno = db_open(buf, dbtype, flags, DBMMODE,
+ NULL, openinfo, &db);
+ }
+#endif
+ saveerrno = errno;
+
+#if !LOCK_ON_OPEN
+ if (mode == O_RDWR)
+ map->map_lockfd = fd;
+ else
+ (void) close(fd);
+#endif
+
+ if (db == NULL)
+ {
+ if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
+ aliaswait(map, ".db", FALSE))
+ return TRUE;
+#if !LOCK_ON_OPEN
+ if (map->map_lockfd >= 0)
+ (void) close(map->map_lockfd);
+#endif
+ errno = saveerrno;
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("Cannot open %s database %s",
+ mapclassname, buf);
+ return FALSE;
+ }
+
+#if DB_VERSION_MAJOR < 2
+ fd = db->fd(db);
+#else
+ fd = -1;
+ errno = db->fd(db, &fd);
+#endif
+ if (filechanged(buf, fd, &st))
+ {
+ int save_errno = errno;
+
+#if DB_VERSION_MAJOR < 2
+ db->close(db);
+#else
+ errno = db->close(db, 0);
+#endif
+#if !LOCK_ON_OPEN
+ if (map->map_lockfd >= 0)
+ close(map->map_lockfd);
+#endif
+ errno = save_errno;
+ syserr("db_map_open(%s): file changed after open", buf);
+ return FALSE;
+ }
+
+ if (mode == O_RDWR)
+ map->map_mflags |= MF_LOCKED;
+#if LOCK_ON_OPEN
+ if (fd >= 0 && mode == O_RDONLY)
+ {
+ (void) lockfile(fd, buf, NULL, LOCK_UN);
+ }
+#endif
+
+ /* try to make sure that at least the database header is on disk */
+ if (mode == O_RDWR)
+ {
+ (void) db->sync(db, 0);
+ if (geteuid() == 0 && TrustedFileUid != 0)
+ {
+ if (fchown(fd, TrustedFileUid, -1) < 0)
+ {
+ int err = errno;
+
+ sm_syslog(LOG_ALERT, NOQID,
+ "ownership change on %s failed: %s",
+ buf, errstring(err));
+ message("050 ownership change on %s failed: %s",
+ buf, errstring(err));
+ }
+ }
+ }
+
+ if (fd >= 0 && fstat(fd, &st) >= 0)
+ map->map_mtime = st.st_mtime;
+
+ map->map_db2 = (ARBPTR_T) db;
+ if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
+ !aliaswait(map, ".db", TRUE))
+ return FALSE;
+ return TRUE;
+}
+
+
+/*
+** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
+*/
+
+char *
+db_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ DBT key, val;
+ register DB *db = (DB *) map->map_db2;
+ int i;
+ int st;
+ int saveerrno;
+ int fd;
+ struct stat stbuf;
+ char keybuf[MAXNAME + 1];
+ char buf[MAXNAME + 1];
+
+ bzero(&key, sizeof key);
+ bzero(&val, sizeof val);
+
+ if (tTd(38, 20))
+ printf("db_map_lookup(%s, %s)\n",
+ map->map_mname, name);
+
+ i = strlen(map->map_file);
+ if (i > MAXNAME)
+ i = MAXNAME;
+ strncpy(buf, map->map_file, i);
+ buf[i] = '\0';
+ if (i > 3 && strcmp(&buf[i - 3], ".db") == 0)
+ buf[i - 3] = '\0';
+
+ key.size = strlen(name);
+ if (key.size > sizeof keybuf - 1)
+ key.size = sizeof keybuf - 1;
+ key.data = keybuf;
+ bcopy(name, keybuf, key.size);
+ keybuf[key.size] = '\0';
+ if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+ makelower(keybuf);
+ lockdb:
+#if DB_VERSION_MAJOR < 2
+ fd = db->fd(db);
+#else
+ fd = -1;
+ errno = db->fd(db, &fd);
+#endif
+ if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+ (void) lockfile(fd, buf, ".db", LOCK_SH);
+ if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime)
+ {
+ /* Reopen the database to sync the cache */
+ int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR
+ : O_RDONLY;
+
+ map->map_class->map_close(map);
+ map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+ if (map->map_class->map_open(map, omode))
+ {
+ map->map_mflags |= MF_OPEN;
+ if ((omode && O_ACCMODE) == O_RDWR)
+ map->map_mflags |= MF_WRITABLE;
+ db = (DB *) map->map_db2;
+ goto lockdb;
+ }
+ else
+ {
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ extern MAPCLASS BogusMapClass;
+
+ *statp = EX_TEMPFAIL;
+ map->map_class = &BogusMapClass;
+ map->map_mflags |= MF_OPEN;
+ syserr("Cannot reopen DB database %s",
+ map->map_file);
+ }
+ return NULL;
+ }
+ }
+
+ st = 1;
+ if (bitset(MF_TRY0NULL, map->map_mflags))
+ {
+#if DB_VERSION_MAJOR < 2
+ st = db->get(db, &key, &val, 0);
+#else
+ errno = db->get(db, NULL, &key, &val, 0);
+ switch (errno)
+ {
+ case DB_NOTFOUND:
+ case DB_KEYEMPTY:
+ st = 1;
+ break;
+
+ case 0:
+ st = 0;
+ break;
+
+ default:
+ st = -1;
+ break;
+ }
+#endif
+ if (st == 0)
+ map->map_mflags &= ~MF_TRY1NULL;
+ }
+ if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
+ {
+ key.size++;
+#if DB_VERSION_MAJOR < 2
+ st = db->get(db, &key, &val, 0);
+#else
+ errno = db->get(db, NULL, &key, &val, 0);
+ switch (errno)
+ {
+ case DB_NOTFOUND:
+ case DB_KEYEMPTY:
+ st = 1;
+ break;
+
+ case 0:
+ st = 0;
+ break;
+
+ default:
+ st = -1;
+ break;
+ }
+#endif
+ if (st == 0)
+ map->map_mflags &= ~MF_TRY0NULL;
+ }
+ saveerrno = errno;
+ if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+ (void) lockfile(fd, buf, ".db", LOCK_UN);
+ if (st != 0)
+ {
+ errno = saveerrno;
+ if (st < 0)
+ syserr("db_map_lookup: get (%s)", name);
+ return NULL;
+ }
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ return map_rewrite(map, name, strlen(name), NULL);
+ else
+ return map_rewrite(map, val.data, val.size, av);
+}
+
+
+/*
+** DB_MAP_STORE -- store a datum in the NEWDB database
+*/
+
+void
+db_map_store(map, lhs, rhs)
+ register MAP *map;
+ char *lhs;
+ char *rhs;
+{
+ int stat;
+ DBT key;
+ DBT data;
+ register DB *db = map->map_db2;
+ char keybuf[MAXNAME + 1];
+
+ bzero(&key, sizeof key);
+ bzero(&data, sizeof data);
+
+ if (tTd(38, 12))
+ printf("db_map_store(%s, %s, %s)\n",
+ map->map_mname, lhs, rhs);
+
+ key.size = strlen(lhs);
+ key.data = lhs;
+ if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+ {
+ if (key.size > sizeof keybuf - 1)
+ key.size = sizeof keybuf - 1;
+ bcopy(key.data, keybuf, key.size);
+ keybuf[key.size] = '\0';
+ makelower(keybuf);
+ key.data = keybuf;
+ }
+
+ data.size = strlen(rhs);
+ data.data = rhs;
+
+ if (bitset(MF_INCLNULL, map->map_mflags))
+ {
+ key.size++;
+ data.size++;
+ }
+
+#if DB_VERSION_MAJOR < 2
+ stat = db->put(db, &key, &data, R_NOOVERWRITE);
+#else
+ errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE);
+ switch (errno)
+ {
+ case DB_KEYEXIST:
+ stat = 1;
+ break;
+
+ case 0:
+ stat = 0;
+ break;
+
+ default:
+ stat = -1;
+ break;
+ }
+#endif
+ if (stat > 0)
+ {
+ if (!bitset(MF_APPEND, map->map_mflags))
+ message("050 Warning: duplicate alias name %s", lhs);
+ else
+ {
+ static char *buf = NULL;
+ static int bufsiz = 0;
+ DBT old;
+
+ bzero(&old, sizeof old);
+
+ old.data = db_map_lookup(map, key.data,
+ (char **)NULL, &stat);
+ if (old.data != NULL)
+ {
+ old.size = strlen(old.data);
+ if (data.size + old.size + 2 > bufsiz)
+ {
+ if (buf != NULL)
+ (void) free(buf);
+ bufsiz = data.size + old.size + 2;
+ buf = xalloc(bufsiz);
+ }
+ snprintf(buf, bufsiz, "%s,%s",
+ (char *) data.data, (char *) old.data);
+ data.size = data.size + old.size + 1;
+ data.data = buf;
+ if (tTd(38, 9))
+ printf("db_map_store append=%s\n",
+ (char *) data.data);
+ }
+ }
+#if DB_VERSION_MAJOR < 2
+ stat = db->put(db, &key, &data, 0);
+#else
+ stat = errno = db->put(db, NULL, &key, &data, 0);
+#endif
+ }
+ if (stat != 0)
+ syserr("readaliases: db put (%s)", lhs);
+}
+
+
+/*
+** DB_MAP_CLOSE -- add distinguished entries and close the database
+*/
+
+void
+db_map_close(map)
+ MAP *map;
+{
+ register DB *db = map->map_db2;
+
+ if (tTd(38, 9))
+ printf("db_map_close(%s, %s, %lx)\n",
+ map->map_mname, map->map_file, map->map_mflags);
+
+ if (bitset(MF_WRITABLE, map->map_mflags))
+ {
+ /* write out the distinguished alias */
+ db_map_store(map, "@", "@");
+ }
+
+ (void) db->sync(db, 0);
+
+#if !LOCK_ON_OPEN
+ if (map->map_lockfd >= 0)
+ (void) close(map->map_lockfd);
+#endif
+
+#if DB_VERSION_MAJOR < 2
+ if (db->close(db) != 0)
+#else
+ if ((errno = db->close(db, 0)) != 0)
+#endif
+ syserr("readaliases: db close failure");
+}
+
+#endif
+ /*
+** NIS Modules
+*/
+
+# ifdef NIS
+
+# ifndef YPERR_BUSY
+# define YPERR_BUSY 16
+# endif
+
+/*
+** NIS_MAP_OPEN -- open DBM map
+*/
+
+bool
+nis_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ int yperr;
+ register char *p;
+ auto char *vp;
+ auto int vsize;
+
+ if (tTd(38, 2))
+ printf("nis_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+
+ mode &= O_ACCMODE;
+ if (mode != O_RDONLY)
+ {
+ /* issue a pseudo-error message */
+#ifdef ENOSYS
+ errno = ENOSYS;
+#else
+# ifdef EFTYPE
+ errno = EFTYPE;
+# else
+ errno = ENXIO;
+# endif
+#endif
+ return FALSE;
+ }
+
+ p = strchr(map->map_file, '@');
+ if (p != NULL)
+ {
+ *p++ = '\0';
+ if (*p != '\0')
+ map->map_domain = p;
+ }
+
+ if (*map->map_file == '\0')
+ map->map_file = "mail.aliases";
+
+ if (map->map_domain == NULL)
+ {
+ yperr = yp_get_default_domain(&map->map_domain);
+ if (yperr != 0)
+ {
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("421 NIS map %s specified, but NIS not running",
+ map->map_file);
+ return FALSE;
+ }
+ }
+
+ /* check to see if this map actually exists */
+ yperr = yp_match(map->map_domain, map->map_file, "@", 1,
+ &vp, &vsize);
+ if (tTd(38, 10))
+ printf("nis_map_open: yp_match(@, %s, %s) => %s\n",
+ map->map_domain, map->map_file, yperr_string(yperr));
+ if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY)
+ {
+ /*
+ ** We ought to be calling aliaswait() here if this is an
+ ** alias file, but powerful HP-UX NIS servers apparently
+ ** don't insert the @:@ token into the alias map when it
+ ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
+ */
+
+#if 0
+ if (!bitset(MF_ALIAS, map->map_mflags) ||
+ aliaswait(map, NULL, TRUE))
+#endif
+ return TRUE;
+ }
+
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ syserr("421 Cannot bind to map %s in domain %s: %s",
+ map->map_file, map->map_domain, yperr_string(yperr));
+ }
+
+ return FALSE;
+}
+
+
+/*
+** NIS_MAP_LOOKUP -- look up a datum in a NIS map
+*/
+
+/* ARGSUSED3 */
+char *
+nis_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ char *vp;
+ auto int vsize;
+ int buflen;
+ int yperr;
+ char keybuf[MAXNAME + 1];
+
+ if (tTd(38, 20))
+ printf("nis_map_lookup(%s, %s)\n",
+ map->map_mname, name);
+
+ buflen = strlen(name);
+ if (buflen > sizeof keybuf - 1)
+ buflen = sizeof keybuf - 1;
+ bcopy(name, keybuf, buflen);
+ keybuf[buflen] = '\0';
+ if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+ makelower(keybuf);
+ yperr = YPERR_KEY;
+ if (bitset(MF_TRY0NULL, map->map_mflags))
+ {
+ yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
+ &vp, &vsize);
+ if (yperr == 0)
+ map->map_mflags &= ~MF_TRY1NULL;
+ }
+ if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags))
+ {
+ buflen++;
+ yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen,
+ &vp, &vsize);
+ if (yperr == 0)
+ map->map_mflags &= ~MF_TRY0NULL;
+ }
+ if (yperr != 0)
+ {
+ if (yperr != YPERR_KEY && yperr != YPERR_BUSY)
+ map->map_mflags &= ~(MF_VALID|MF_OPEN);
+ return NULL;
+ }
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ return map_rewrite(map, name, strlen(name), NULL);
+ else
+ return map_rewrite(map, vp, vsize, av);
+}
+
+
+/*
+** NIS_GETCANONNAME -- look up canonical name in NIS
+*/
+
+bool
+nis_getcanonname(name, hbsize, statp)
+ char *name;
+ int hbsize;
+ int *statp;
+{
+ char *vp;
+ auto int vsize;
+ int keylen;
+ int yperr;
+ static bool try0null = TRUE;
+ static bool try1null = TRUE;
+ static char *yp_domain = NULL;
+ char host_record[MAXLINE];
+ char cbuf[MAXNAME];
+ char nbuf[MAXNAME + 1];
+
+ if (tTd(38, 20))
+ printf("nis_getcanonname(%s)\n", name);
+
+ if (strlen(name) >= sizeof nbuf)
+ {
+ *statp = EX_UNAVAILABLE;
+ return FALSE;
+ }
+ (void) strcpy(nbuf, name);
+ shorten_hostname(nbuf);
+ keylen = strlen(nbuf);
+
+ if (yp_domain == NULL)
+ yp_get_default_domain(&yp_domain);
+ makelower(nbuf);
+ yperr = YPERR_KEY;
+ if (try0null)
+ {
+ yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
+ &vp, &vsize);
+ if (yperr == 0)
+ try1null = FALSE;
+ }
+ if (yperr == YPERR_KEY && try1null)
+ {
+ keylen++;
+ yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen,
+ &vp, &vsize);
+ if (yperr == 0)
+ try0null = FALSE;
+ }
+ if (yperr != 0)
+ {
+ if (yperr == YPERR_KEY)
+ *statp = EX_NOHOST;
+ else if (yperr == YPERR_BUSY)
+ *statp = EX_TEMPFAIL;
+ else
+ *statp = EX_UNAVAILABLE;
+ return FALSE;
+ }
+ if (vsize >= sizeof host_record)
+ vsize = sizeof host_record - 1;
+ strncpy(host_record, vp, vsize);
+ host_record[vsize] = '\0';
+ if (tTd(38, 44))
+ printf("got record `%s'\n", host_record);
+ if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf))
+ {
+ /* this should not happen, but.... */
+ *statp = EX_NOHOST;
+ return FALSE;
+ }
+ if (hbsize < strlen(cbuf))
+ {
+ *statp = EX_UNAVAILABLE;
+ return FALSE;
+ }
+ strcpy(name, cbuf);
+ *statp = EX_OK;
+ return TRUE;
+}
+
+#endif
+ /*
+** NISPLUS Modules
+**
+** This code donated by Sun Microsystems.
+*/
+
+#ifdef NISPLUS
+
+#undef NIS /* symbol conflict in nis.h */
+#undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
+#include <rpcsvc/nis.h>
+#include <rpcsvc/nislib.h>
+
+#define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
+#define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
+#define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
+#define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
+
+/*
+** NISPLUS_MAP_OPEN -- open nisplus table
+*/
+
+bool
+nisplus_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ nis_result *res = NULL;
+ int retry_cnt, max_col, i;
+ char qbuf[MAXLINE + NIS_MAXNAMELEN];
+
+ if (tTd(38, 2))
+ printf("nisplus_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+
+ mode &= O_ACCMODE;
+ if (mode != O_RDONLY)
+ {
+ errno = EPERM;
+ return FALSE;
+ }
+
+ if (*map->map_file == '\0')
+ map->map_file = "mail_aliases.org_dir";
+
+ if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
+ {
+ /* set default NISPLUS Domain to $m */
+ extern char *nisplus_default_domain __P((void));
+
+ map->map_domain = newstr(nisplus_default_domain());
+ if (tTd(38, 2))
+ printf("nisplus_map_open(%s): using domain %s\n",
+ map->map_file, map->map_domain);
+ }
+ if (!PARTIAL_NAME(map->map_file))
+ {
+ map->map_domain = newstr("");
+ snprintf(qbuf, sizeof qbuf, "%s", map->map_file);
+ }
+ else
+ {
+ /* check to see if this map actually exists */
+ snprintf(qbuf, sizeof qbuf, "%s.%s",
+ map->map_file, map->map_domain);
+ }
+
+ retry_cnt = 0;
+ while (res == NULL || res->status != NIS_SUCCESS)
+ {
+ res = nis_lookup(qbuf, FOLLOW_LINKS);
+ switch (res->status)
+ {
+ case NIS_SUCCESS:
+ break;
+
+ case NIS_TRYAGAIN:
+ case NIS_RPCERROR:
+ case NIS_NAMEUNREACHABLE:
+ if (retry_cnt++ > 4)
+ {
+ errno = EAGAIN;
+ return FALSE;
+ }
+ /* try not to overwhelm hosed server */
+ sleep(2);
+ break;
+
+ default: /* all other nisplus errors */
+#if 0
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("421 Cannot find table %s.%s: %s",
+ map->map_file, map->map_domain,
+ nis_sperrno(res->status));
+#endif
+ errno = EAGAIN;
+ return FALSE;
+ }
+ }
+
+ if (NIS_RES_NUMOBJ(res) != 1 ||
+ (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ))
+ {
+ if (tTd(38, 10))
+ printf("nisplus_map_open: %s is not a table\n", qbuf);
+#if 0
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("421 %s.%s: %s is not a table",
+ map->map_file, map->map_domain,
+ nis_sperrno(res->status));
+#endif
+ errno = EBADF;
+ return FALSE;
+ }
+ /* default key column is column 0 */
+ if (map->map_keycolnm == NULL)
+ map->map_keycolnm = newstr(COL_NAME(res,0));
+
+ max_col = COL_MAX(res);
+
+ /* verify the key column exist */
+ for (i=0; i< max_col; i++)
+ {
+ if (!strcmp(map->map_keycolnm, COL_NAME(res,i)))
+ break;
+ }
+ if (i == max_col)
+ {
+ if (tTd(38, 2))
+ printf("nisplus_map_open(%s): can not find key column %s\n",
+ map->map_file, map->map_keycolnm);
+ errno = ENOENT;
+ return FALSE;
+ }
+
+ /* default value column is the last column */
+ if (map->map_valcolnm == NULL)
+ {
+ map->map_valcolno = max_col - 1;
+ return TRUE;
+ }
+
+ for (i=0; i< max_col; i++)
+ {
+ if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0)
+ {
+ map->map_valcolno = i;
+ return TRUE;
+ }
+ }
+
+ if (tTd(38, 2))
+ printf("nisplus_map_open(%s): can not find column %s\n",
+ map->map_file, map->map_keycolnm);
+ errno = ENOENT;
+ return FALSE;
+}
+
+
+/*
+** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
+*/
+
+char *
+nisplus_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ char *p;
+ auto int vsize;
+ char *skp;
+ int skleft;
+ char search_key[MAXNAME + 4];
+ char qbuf[MAXLINE + NIS_MAXNAMELEN];
+ nis_result *result;
+
+ if (tTd(38, 20))
+ printf("nisplus_map_lookup(%s, %s)\n",
+ map->map_mname, name);
+
+ if (!bitset(MF_OPEN, map->map_mflags))
+ {
+ if (nisplus_map_open(map, O_RDONLY))
+ map->map_mflags |= MF_OPEN;
+ else
+ {
+ *statp = EX_UNAVAILABLE;
+ return NULL;
+ }
+ }
+
+ /*
+ ** Copy the name to the key buffer, escaping double quote characters
+ ** by doubling them and quoting "]" and "," to avoid having the
+ ** NIS+ parser choke on them.
+ */
+
+ skleft = sizeof search_key - 4;
+ skp = search_key;
+ for (p = name; *p != '\0' && skleft > 0; p++)
+ {
+ switch (*p)
+ {
+ case ']':
+ case ',':
+ /* quote the character */
+ *skp++ = '"';
+ *skp++ = *p;
+ *skp++ = '"';
+ skleft -= 3;
+ break;
+
+ case '"':
+ /* double the quote */
+ *skp++ = '"';
+ skleft--;
+ /* fall through... */
+
+ default:
+ *skp++ = *p;
+ skleft--;
+ break;
+ }
+ }
+ *skp = '\0';
+ if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+ makelower(search_key);
+
+ /* construct the query */
+ if (PARTIAL_NAME(map->map_file))
+ snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s",
+ map->map_keycolnm, search_key, map->map_file,
+ map->map_domain);
+ else
+ snprintf(qbuf, sizeof qbuf, "[%s=%s],%s",
+ map->map_keycolnm, search_key, map->map_file);
+
+ if (tTd(38, 20))
+ printf("qbuf=%s\n", qbuf);
+ result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
+ if (result->status == NIS_SUCCESS)
+ {
+ int count;
+ char *str;
+
+ if ((count = NIS_RES_NUMOBJ(result)) != 1)
+ {
+ if (LogLevel > 10)
+ sm_syslog(LOG_WARNING, CurEnv->e_id,
+ "%s: lookup error, expected 1 entry, got %d",
+ map->map_file, count);
+
+ /* ignore second entry */
+ if (tTd(38, 20))
+ printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
+ name, count);
+ }
+
+ p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
+ /* set the length of the result */
+ if (p == NULL)
+ p = "";
+ vsize = strlen(p);
+ if (tTd(38, 20))
+ printf("nisplus_map_lookup(%s), found %s\n",
+ name, p);
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ str = map_rewrite(map, name, strlen(name), NULL);
+ else
+ str = map_rewrite(map, p, vsize, av);
+ nis_freeresult(result);
+ *statp = EX_OK;
+ return str;
+ }
+ else
+ {
+ if (result->status == NIS_NOTFOUND)
+ *statp = EX_NOTFOUND;
+ else if (result->status == NIS_TRYAGAIN)
+ *statp = EX_TEMPFAIL;
+ else
+ {
+ *statp = EX_UNAVAILABLE;
+ map->map_mflags &= ~(MF_VALID|MF_OPEN);
+ }
+ }
+ if (tTd(38, 20))
+ printf("nisplus_map_lookup(%s), failed\n", name);
+ nis_freeresult(result);
+ return NULL;
+}
+
+
+
+/*
+** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
+*/
+
+bool
+nisplus_getcanonname(name, hbsize, statp)
+ char *name;
+ int hbsize;
+ int *statp;
+{
+ char *vp;
+ auto int vsize;
+ nis_result *result;
+ char *p;
+ char nbuf[MAXNAME + 1];
+ char qbuf[MAXLINE + NIS_MAXNAMELEN];
+
+ if (strlen(name) >= sizeof nbuf)
+ {
+ *statp = EX_UNAVAILABLE;
+ return FALSE;
+ }
+ (void) strcpy(nbuf, name);
+ shorten_hostname(nbuf);
+
+ p = strchr(nbuf, '.');
+ if (p == NULL)
+ {
+ /* single token */
+ snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf);
+ }
+ else if (p[1] != '\0')
+ {
+ /* multi token -- take only first token in nbuf */
+ *p = '\0';
+ snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s",
+ nbuf, &p[1]);
+ }
+ else
+ {
+ *statp = EX_NOHOST;
+ return FALSE;
+ }
+
+ if (tTd(38, 20))
+ printf("\nnisplus_getcanoname(%s), qbuf=%s\n",
+ name, qbuf);
+
+ result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH,
+ NULL, NULL);
+
+ if (result->status == NIS_SUCCESS)
+ {
+ int count;
+ char *domain;
+
+ if ((count = NIS_RES_NUMOBJ(result)) != 1)
+ {
+ if (LogLevel > 10)
+ sm_syslog(LOG_WARNING, CurEnv->e_id,
+ "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
+ count);
+
+ /* ignore second entry */
+ if (tTd(38, 20))
+ printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n",
+ name, count);
+ }
+
+ if (tTd(38, 20))
+ printf("nisplus_getcanoname(%s), found in directory \"%s\"\n",
+ name, (NIS_RES_OBJECT(result))->zo_domain);
+
+
+ vp = ((NIS_RES_OBJECT(result))->EN_col(0));
+ vsize = strlen(vp);
+ if (tTd(38, 20))
+ printf("nisplus_getcanonname(%s), found %s\n",
+ name, vp);
+ if (strchr(vp, '.') != NULL)
+ {
+ domain = "";
+ }
+ else
+ {
+ domain = macvalue('m', CurEnv);
+ if (domain == NULL)
+ domain = "";
+ }
+ if (hbsize > vsize + (int) strlen(domain) + 1)
+ {
+ if (domain[0] == '\0')
+ strcpy(name, vp);
+ else
+ snprintf(name, hbsize, "%s.%s", vp, domain);
+ *statp = EX_OK;
+ }
+ else
+ *statp = EX_NOHOST;
+ nis_freeresult(result);
+ return TRUE;
+ }
+ else
+ {
+ if (result->status == NIS_NOTFOUND)
+ *statp = EX_NOHOST;
+ else if (result->status == NIS_TRYAGAIN)
+ *statp = EX_TEMPFAIL;
+ else
+ *statp = EX_UNAVAILABLE;
+ }
+ if (tTd(38, 20))
+ printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
+ name, result->status, *statp);
+ nis_freeresult(result);
+ return FALSE;
+}
+
+
+char *
+nisplus_default_domain()
+{
+ static char default_domain[MAXNAME + 1] = "";
+ char *p;
+
+ if (default_domain[0] != '\0')
+ return(default_domain);
+
+ p = nis_local_directory();
+ snprintf(default_domain, sizeof default_domain, "%s", p);
+ return default_domain;
+}
+
+#endif /* NISPLUS */
+ /*
+** LDAP Modules
+**
+** Contributed by Booker C. Bense <bbense@networking.stanford.edu>.
+** Get your support from him.
+*/
+
+#ifdef LDAPMAP
+
+# undef NEEDGETOPT /* used for something else in LDAP */
+
+# include <lber.h>
+# include <ldap.h>
+# include "ldap_map.h"
+
+/*
+** LDAP_MAP_OPEN -- open LDAP map
+**
+** Since LDAP is TCP-based there is not much we can or should do
+** here. It might be a good idea to attempt an open/close here.
+*/
+
+bool
+ldap_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ if (tTd(38, 2))
+ printf("ldap_map_open(%s, %d)\n", map->map_mname, mode);
+
+ mode &= O_ACCMODE;
+ if (mode != O_RDONLY)
+ {
+ /* issue a pseudo-error message */
+#ifdef ENOSYS
+ errno = ENOSYS;
+#else
+# ifdef EFTYPE
+ errno = EFTYPE;
+# else
+ errno = ENXIO;
+# endif
+#endif
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+** LDAP_MAP_START -- actually open LDAP map
+**
+** Caching should be investigated.
+*/
+
+static jmp_buf LDAPTimeout;
+
+static void
+ldaptimeout(sig_no)
+ int sig_no;
+{
+ longjmp(LDAPTimeout, 1);
+}
+
+bool
+ldap_map_start(map)
+ MAP *map;
+{
+ LDAP_MAP_STRUCT *lmap;
+ LDAP *ld;
+ register EVENT *ev = NULL;
+
+ if (tTd(38, 2))
+ printf("ldap_map_start(%s)\n", map->map_mname);
+
+ lmap = (LDAP_MAP_STRUCT *) map->map_db1;
+
+ if (tTd(38,9))
+ printf("ldap_open(%s, %d)\n", lmap->ldaphost, lmap->ldapport);
+
+ /* Need to set an alarm here, ldap_open is hopelessly broken. */
+
+ /* set the timeout */
+ if (lmap->timeout.tv_sec != 0)
+ {
+ if (setjmp(LDAPTimeout) != 0)
+ {
+ if (LogLevel > 1)
+ sm_syslog(LOG_NOTICE, CurEnv->e_id,
+ "timeout waiting for ldap_open to %.100s",
+ lmap->ldaphost);
+ return (FALSE);
+ }
+ ev = setevent(lmap->timeout.tv_sec, ldaptimeout, 0);
+ }
+
+ if ((ld = ldap_open(lmap->ldaphost,lmap->ldapport)) == NULL)
+ {
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ syserr("ldapopen failed to %s in map %s",
+ lmap->ldaphost, map->map_mname);
+ }
+ return FALSE;
+ }
+
+ /* clear the event if it has not sprung */
+ clrevent(ev);
+ /* From here on in we can use ldap internal timelimits */
+ ld->ld_deref = lmap->deref;
+ ld->ld_timelimit = lmap->timelimit;
+ ld->ld_sizelimit = lmap->sizelimit;
+ ld->ld_options = lmap->ldap_options;
+
+ if (ldap_bind_s(ld, lmap->binddn,lmap->passwd,lmap->method) != LDAP_SUCCESS)
+ {
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ syserr("421 Cannot bind to map %s in ldap server %s",
+ map->map_mname, lmap->ldaphost);
+ }
+ }
+ else
+ {
+ /* We need to cast ld into the map structure */
+ lmap->ld = ld;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+** LDAP_MAP_CLOSE -- close ldap map
+*/
+
+void
+ldap_map_close(map)
+ MAP *map;
+{
+ LDAP_MAP_STRUCT *lmap ;
+ lmap = (LDAP_MAP_STRUCT *) map->map_db1;
+ if (lmap->ld != NULL)
+ ldap_unbind(lmap->ld);
+}
+
+
+#ifdef SUNET_ID
+/*
+** SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form
+** This only makes sense at Stanford University.
+*/
+
+char *
+sunet_id_hash(str)
+ char *str;
+{
+ char *p, *p_last;
+
+ p = str;
+ p_last = p;
+ while (*p != '\0')
+ {
+ if (islower(*p) || isdigit(*p))
+ {
+ *p_last = *p;
+ p_last++;
+ }
+ else if (isupper(*p))
+ {
+ *p_last = tolower(*p);
+ p_last++;
+ }
+ ++p;
+ }
+ if (*p_last != '\0')
+ *p_last = '\0';
+ return (str);
+}
+
+
+
+#endif /* SUNET_ID */
+/*
+** LDAP_MAP_LOOKUP -- look up a datum in a LDAP map
+*/
+
+char *
+ldap_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ LDAP_MAP_STRUCT *lmap = NULL;
+ LDAPMessage *entry;
+ char *vp;
+ auto int vsize;
+ char keybuf[MAXNAME + 1];
+ char filter[LDAP_MAP_MAX_FILTER + 1];
+ char **attr_values = NULL;
+ char *result;
+ int name_len;
+
+ if (tTd(38, 20))
+ printf("ldap_map_lookup(%s, %s)\n", map->map_mname, name);
+
+ /* actually open the map */
+ if (!ldap_map_start(map))
+ {
+ result = NULL;
+ *statp = EX_TEMPFAIL;
+ goto quick_exit;
+ }
+
+ /* Get ldap struct pointer from map */
+ lmap = (LDAP_MAP_STRUCT *) map->map_db1;
+
+ name_len = strlen(name);
+ if (name_len > MAXNAME)
+ name_len = MAXNAME;
+ strncpy(keybuf, name, name_len);
+ keybuf[name_len] = '\0';
+
+ if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+#ifdef SUNET_ID
+ sunet_id_hash(keybuf);
+#else
+ makelower(keybuf);
+#endif /*SUNET_ID */
+
+ /* sprintf keybuf into filter */
+ snprintf(filter, sizeof filter, lmap->filter, keybuf);
+
+ if (ldap_search_st(lmap->ld, lmap->base,lmap->scope,filter,
+ lmap->attr, lmap->attrsonly, &(lmap->timeout),
+ &(lmap->res)) != LDAP_SUCCESS)
+ {
+ /* try close/opening map */
+ ldap_map_close(map);
+ if (!ldap_map_start(map))
+ {
+ result = NULL;
+ *statp = EX_TEMPFAIL;
+ goto quick_exit;
+ }
+ if (ldap_search_st(lmap->ld, lmap->base, lmap->scope, filter,
+ lmap->attr, lmap->attrsonly,
+ &(lmap->timeout), &(lmap->res))
+ != LDAP_SUCCESS)
+ {
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ {
+ syserr("Error in ldap_search_st using %s in map %s",
+ filter, map->map_mname);
+ }
+ result = NULL;
+ *statp = EX_TEMPFAIL;
+ goto quick_exit;
+ }
+ }
+
+ entry = ldap_first_entry(lmap->ld,lmap->res);
+ if (entry == NULL)
+ {
+ result = NULL;
+ *statp = EX_NOTFOUND;
+ goto quick_exit;
+ }
+
+ /* Need to build the args for map_rewrite here */
+ attr_values = ldap_get_values(lmap->ld,entry,lmap->attr[0]);
+ if (attr_values == NULL)
+ {
+ /* bad things happened */
+ result = NULL;
+ *statp = EX_NOTFOUND;
+ goto quick_exit;
+ }
+
+ *statp = EX_OK;
+
+ /* If there is more that one use the first */
+ vp = attr_values[0];
+ vsize = strlen(vp);
+
+ if (LogLevel > 9)
+ sm_syslog(LOG_INFO, CurEnv->e_id,
+ "ldap %.100s => %s",
+ name, vp);
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ result = map_rewrite(map, name, strlen(name), NULL);
+ else
+ result = map_rewrite(map, vp, vsize, av);
+
+ quick_exit:
+ if (attr_values != NULL)
+ ldap_value_free(attr_values);
+ if (lmap != NULL)
+ ldap_msgfree(lmap->res);
+ ldap_map_close(map);
+ return result ;
+}
+
+
+/*
+** LDAP_MAP_DEQUOTE - helper routine for ldap_map_parseargs
+*/
+
+char *
+ldap_map_dequote(str)
+ char *str;
+{
+ char *p;
+ char *start;
+ p = str;
+
+ if (*p == '"')
+ {
+ start = ++p;
+ /* Should probably swallow initial whitespace here */
+ }
+ else
+ {
+ return(str);
+ }
+ while (*p != '"' && *p != '\0')
+ {
+ p++;
+ }
+ if (*p != '\0')
+ *p = '\0';
+ return start;
+}
+
+/*
+** LDAP_MAP_PARSEARGS -- parse ldap map definition args.
+*/
+
+bool
+ldap_map_parseargs(map,args)
+ MAP *map;
+ char *args;
+{
+ register char *p = args;
+ register int done;
+ LDAP_MAP_STRUCT *lmap;
+
+ /* We need to alloc an LDAP_MAP_STRUCT struct */
+ lmap = (LDAP_MAP_STRUCT *) xalloc(sizeof(LDAP_MAP_STRUCT));
+
+ /* Set default int's here , default strings below */
+ lmap->ldapport = DEFAULT_LDAP_MAP_PORT;
+ lmap->deref = DEFAULT_LDAP_MAP_DEREF;
+ lmap->timelimit = DEFAULT_LDAP_MAP_TIMELIMIT;
+ lmap->sizelimit = DEFAULT_LDAP_MAP_SIZELIMIT;
+ lmap->ldap_options = DEFAULT_LDAP_MAP_LDAP_OPTIONS;
+ lmap->method = DEFAULT_LDAP_MAP_METHOD;
+ lmap->scope = DEFAULT_LDAP_MAP_SCOPE;
+ lmap->attrsonly = DEFAULT_LDAP_MAP_ATTRSONLY;
+ lmap->timeout.tv_sec = DEFAULT_LDAP_MAP_TIMELIMIT;
+ lmap->timeout.tv_usec = 0;
+
+ /* Default char ptrs to NULL */
+ lmap->binddn = NULL;
+ lmap->passwd = NULL;
+ lmap->base = NULL;
+ lmap->ldaphost = NULL;
+
+ /* Default general ptrs to NULL */
+ lmap->ld = NULL;
+ lmap->res = NULL;
+
+ map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL;
+ for (;;)
+ {
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p != '-')
+ break;
+ switch (*++p)
+ {
+ case 'N':
+ map->map_mflags |= MF_INCLNULL;
+ map->map_mflags &= ~MF_TRY0NULL;
+ break;
+
+ case 'O':
+ map->map_mflags &= ~MF_TRY1NULL;
+ break;
+
+ case 'o':
+ map->map_mflags |= MF_OPTIONAL;
+ break;
+
+ case 'f':
+ map->map_mflags |= MF_NOFOLDCASE;
+ break;
+
+ case 'm':
+ map->map_mflags |= MF_MATCHONLY;
+ break;
+
+ case 'A':
+ map->map_mflags |= MF_APPEND;
+ break;
+
+ case 'q':
+ map->map_mflags |= MF_KEEPQUOTES;
+ break;
+
+ case 't':
+ map->map_mflags |= MF_NODEFER;
+ break;
+
+ case 'a':
+ map->map_app = ++p;
+ break;
+
+ case 'T':
+ map->map_tapp = ++p;
+ break;
+
+ /* Start of ldap_map specific args */
+ case 'k': /* search field */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ lmap->filter = p;
+ break;
+
+ case 'v': /* attr to return */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ lmap->attr[0] = p;
+ lmap->attr[1] = NULL;
+ break;
+
+ /* args stolen from ldapsearch.c */
+ case 'R': /* don't auto chase referrals */
+#ifdef LDAP_REFERRALS
+ lmap->ldap_options &= ~LDAP_OPT_REFERRALS;
+#else /* LDAP_REFERRALS */
+ syserr("compile with -DLDAP_REFERRALS for referral support\n");
+#endif /* LDAP_REFERRALS */
+ break;
+
+ case 'n': /* retrieve attribute names only -- no values */
+ lmap->attrsonly += 1;
+ break;
+
+ case 's': /* search scope */
+ if (strncasecmp(++p, "base", 4) == 0)
+ {
+ lmap->scope = LDAP_SCOPE_BASE;
+ }
+ else if (strncasecmp(p, "one", 3) == 0)
+ {
+ lmap->scope = LDAP_SCOPE_ONELEVEL;
+ }
+ else if (strncasecmp(p, "sub", 3) == 0)
+ {
+ lmap->scope = LDAP_SCOPE_SUBTREE;
+ }
+ else
+ { /* bad config line */
+ if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
+ {
+ char *ptr;
+
+ if ((ptr = strchr(p, ' ')) != NULL)
+ *ptr = '\0';
+ syserr("Scope must be [base|one|sub] not %s in map %s",
+ p, map->map_mname);
+ if (ptr != NULL)
+ *ptr = ' ';
+ return FALSE;
+ }
+ }
+ break;
+
+ case 'h': /* ldap host */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ map->map_domain = p;
+ lmap->ldaphost = p;
+ break;
+
+ case 'b': /* search base */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ lmap->base = p;
+ break;
+
+ case 'p': /* ldap port */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ lmap->ldapport = atoi(p);
+ break;
+
+ case 'l': /* time limit */
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ lmap->timelimit = atoi(p);
+ lmap->timeout.tv_sec = lmap->timelimit;
+ break;
+
+ }
+
+ /* need to account for quoted strings here arggg... */
+ done = isascii(*p) && isspace(*p);
+ while (*p != '\0' && !done)
+ {
+ if (*p == '"')
+ {
+ while (*++p != '"' && *p != '\0')
+ {
+ continue;
+ }
+ if (*p != '\0')
+ p++;
+ }
+ else
+ {
+ p++;
+ }
+ done = isascii(*p) && isspace(*p);
+ }
+
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+
+ if (map->map_app != NULL)
+ map->map_app = newstr(ldap_map_dequote(map->map_app));
+ if (map->map_tapp != NULL)
+ map->map_tapp = newstr(ldap_map_dequote(map->map_tapp));
+ if (map->map_domain != NULL)
+ map->map_domain = newstr(ldap_map_dequote(map->map_domain));
+
+ /*
+ ** We need to swallow up all the stuff into a struct
+ ** and dump it into map->map_dbptr1
+ */
+
+ if (lmap->ldaphost != NULL)
+ lmap->ldaphost = newstr(ldap_map_dequote(lmap->ldaphost));
+ else
+ {
+ syserr("LDAP map: -h flag is required");
+ return FALSE;
+ }
+
+ if (lmap->binddn != NULL)
+ lmap->binddn = newstr(ldap_map_dequote(lmap->binddn));
+ else
+ lmap->binddn = DEFAULT_LDAP_MAP_BINDDN;
+
+
+ if (lmap->passwd != NULL)
+ lmap->passwd = newstr(ldap_map_dequote(lmap->passwd));
+ else
+ lmap->passwd = DEFAULT_LDAP_MAP_PASSWD;
+
+ if (lmap->base != NULL)
+ lmap->base = newstr(ldap_map_dequote(lmap->base));
+ else
+ {
+ syserr("LDAP map: -b flag is required");
+ return FALSE;
+ }
+
+
+ if (lmap->filter != NULL)
+ lmap->filter = newstr(ldap_map_dequote(lmap->filter));
+ else
+ {
+ if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
+ {
+ syserr("No filter given in map %s", map->map_mname);
+ return FALSE;
+ }
+ }
+ if (lmap->attr[0] != NULL)
+ lmap->attr[0] = newstr(ldap_map_dequote(lmap->attr[0]));
+ else
+ {
+ if (!bitset(MCF_OPTFILE, map->map_class->map_cflags))
+ {
+ syserr("No return attribute in %s", map->map_mname);
+ return FALSE;
+ }
+ }
+
+ map->map_db1 = (ARBPTR_T) lmap;
+ return TRUE;
+}
+
+#endif /* LDAP Modules */
+ /*
+** syslog map
+*/
+
+#if _FFR_MAP_SYSLOG
+
+#define map_prio map_lockfd /* overload field */
+
+/*
+** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
+*/
+
+bool
+syslog_map_parseargs(map, args)
+ MAP *map;
+ char *args;
+{
+ char *p = args;
+ char *priority = NULL;
+
+ for (;;)
+ {
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p != '-')
+ break;
+ if (*++p == 'L')
+ priority = ++p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+
+ if (priority == NULL)
+ map->map_prio = LOG_INFO;
+ else
+ {
+ if (strncasecmp("LOG_", priority, 4) == 0)
+ priority += 4;
+
+#ifdef LOG_EMERG
+ if (strcasecmp("EMERG", priority) == 0)
+ map->map_prio = LOG_EMERG;
+ else
+#endif
+#ifdef LOG_ALERT
+ if (strcasecmp("ALERT", priority) == 0)
+ map->map_prio = LOG_ALERT;
+ else
+#endif
+#ifdef LOG_CRIT
+ if (strcasecmp("CRIT", priority) == 0)
+ map->map_prio = LOG_CRIT;
+ else
+#endif
+#ifdef LOG_ERR
+ if (strcasecmp("ERR", priority) == 0)
+ map->map_prio = LOG_ERR;
+ else
+#endif
+#ifdef LOG_WARNING
+ if (strcasecmp("WARNING", priority) == 0)
+ map->map_prio = LOG_WARNING;
+ else
+#endif
+#ifdef LOG_NOTICE
+ if (strcasecmp("NOTICE", priority) == 0)
+ map->map_prio = LOG_NOTICE;
+ else
+#endif
+#ifdef LOG_INFO
+ if (strcasecmp("INFO", priority) == 0)
+ map->map_prio = LOG_INFO;
+ else
+#endif
+#ifdef LOG_DEBUG
+ if (strcasecmp("DEBUG", priority) == 0)
+ map->map_prio = LOG_DEBUG;
+ else
+#endif
+ {
+ syserr("syslog_map_parseargs: Unknown priority %s\n",
+ priority);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
+*/
+
+char *
+syslog_map_lookup(map, string, args, statp)
+ MAP *map;
+ char *string;
+ char **args;
+ int *statp;
+{
+ char *ptr = map_rewrite(map, string, strlen(string), args);
+
+ if (ptr != NULL)
+ {
+ if (tTd(38, 20))
+ printf("syslog_map_lookup(%s (priority %d): %s\n",
+ map->map_mname, map->map_prio, ptr);
+
+ sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr);
+ }
+
+ *statp = EX_OK;
+ return "";
+}
+
+#endif /* _FFR_MAP_SYSLOG */
+ /*
+** HESIOD Modules
+*/
+
+#ifdef HESIOD
+
+bool
+hes_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ if (tTd(38, 2))
+ printf("hes_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+
+ if (mode != O_RDONLY)
+ {
+ /* issue a pseudo-error message */
+#ifdef ENOSYS
+ errno = ENOSYS;
+#else
+# ifdef EFTYPE
+ errno = EFTYPE;
+# else
+ errno = ENXIO;
+# endif
+#endif
+ return FALSE;
+ }
+
+#ifdef HESIOD_INIT
+ if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0)
+ return TRUE;
+
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("421 cannot initialize Hesiod map (%s)",
+ errstring(errno));
+ return FALSE;
+#else
+ if (hes_error() == HES_ER_UNINIT)
+ hes_init();
+ switch (hes_error())
+ {
+ case HES_ER_OK:
+ case HES_ER_NOTFOUND:
+ return TRUE;
+ }
+
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("421 cannot initialize Hesiod map (%d)", hes_error());
+
+ return FALSE;
+#endif /* HESIOD_INIT */
+}
+
+char *
+hes_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ char **hp;
+
+ if (tTd(38, 20))
+ printf("hes_map_lookup(%s, %s)\n", map->map_file, name);
+
+ if (name[0] == '\\')
+ {
+ char *np;
+ int nl;
+ char nbuf[MAXNAME];
+
+ nl = strlen(name);
+ if (nl < sizeof nbuf - 1)
+ np = nbuf;
+ else
+ np = xalloc(strlen(name) + 2);
+ np[0] = '\\';
+ strcpy(&np[1], name);
+#ifdef HESIOD_INIT
+ hp = hesiod_resolve(HesiodContext, np, map->map_file);
+#else
+ hp = hes_resolve(np, map->map_file);
+#endif /* HESIOD_INIT */
+ if (np != nbuf)
+ free(np);
+ }
+ else
+ {
+#ifdef HESIOD_INIT
+ hp = hesiod_resolve(HesiodContext, name, map->map_file);
+#else
+ hp = hes_resolve(name, map->map_file);
+#endif /* HESIOD_INIT */
+ }
+#ifdef HESIOD_INIT
+ if (hp == NULL)
+ return NULL;
+ if (*hp == NULL)
+ {
+ hesiod_free_list(HesiodContext, hp);
+ switch (errno)
+ {
+ case ENOENT:
+ *statp = EX_NOTFOUND;
+ break;
+ case ECONNREFUSED:
+ case EMSGSIZE:
+ *statp = EX_TEMPFAIL;
+ break;
+ case ENOMEM:
+ default:
+ *statp = EX_UNAVAILABLE;
+ break;
+ }
+ return NULL;
+ }
+#else
+ if (hp == NULL || hp[0] == NULL)
+ {
+ switch (hes_error())
+ {
+ case HES_ER_OK:
+ *statp = EX_OK;
+ break;
+
+ case HES_ER_NOTFOUND:
+ *statp = EX_NOTFOUND;
+ break;
+
+ case HES_ER_CONFIG:
+ *statp = EX_UNAVAILABLE;
+ break;
+
+ case HES_ER_NET:
+ *statp = EX_TEMPFAIL;
+ break;
+ }
+ return NULL;
+ }
+#endif /* HESIOD_INIT */
+
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ return map_rewrite(map, name, strlen(name), NULL);
+ else
+ return map_rewrite(map, hp[0], strlen(hp[0]), av);
+}
+
+#endif
+ /*
+** NeXT NETINFO Modules
+*/
+
+#if NETINFO
+
+# define NETINFO_DEFAULT_DIR "/aliases"
+# define NETINFO_DEFAULT_PROPERTY "members"
+
+extern char *ni_propval __P((char *, char *, char *, char *, int));
+
+
+/*
+** NI_MAP_OPEN -- open NetInfo Aliases
+*/
+
+bool
+ni_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ char *p;
+
+ if (tTd(38, 2))
+ printf("ni_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+ mode &= O_ACCMODE;
+
+ if (*map->map_file == '\0')
+ map->map_file = NETINFO_DEFAULT_DIR;
+
+ if (map->map_valcolnm == NULL)
+ map->map_valcolnm = NETINFO_DEFAULT_PROPERTY;
+
+ if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags))
+ map->map_coldelim = ',';
+
+ return TRUE;
+}
+
+
+/*
+** NI_MAP_LOOKUP -- look up a datum in NetInfo
+*/
+
+char *
+ni_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ char *res;
+ char *propval;
+
+ if (tTd(38, 20))
+ printf("ni_map_lookup(%s, %s)\n", map->map_mname, name);
+
+ propval = ni_propval(map->map_file, map->map_keycolnm, name,
+ map->map_valcolnm, map->map_coldelim);
+
+ if (propval == NULL)
+ return NULL;
+
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ res = map_rewrite(map, name, strlen(name), NULL);
+ else
+ res = map_rewrite(map, propval, strlen(propval), av);
+ free(propval);
+ return res;
+}
+
+
+bool
+ni_getcanonname(name, hbsize, statp)
+ char *name;
+ int hbsize;
+ int *statp;
+{
+ char *vptr;
+ char *ptr;
+ char nbuf[MAXNAME + 1];
+
+ if (tTd(38, 20))
+ printf("ni_getcanonname(%s)\n", name);
+
+ if (strlen(name) >= sizeof nbuf)
+ {
+ *statp = EX_UNAVAILABLE;
+ return FALSE;
+ }
+ (void) strcpy(nbuf, name);
+ shorten_hostname(nbuf);
+
+ /* we only accept single token search key */
+ if (strchr(nbuf, '.'))
+ {
+ *statp = EX_NOHOST;
+ return FALSE;
+ }
+
+ /* Do the search */
+ vptr = ni_propval("/machines", NULL, nbuf, "name", '\n');
+
+ if (vptr == NULL)
+ {
+ *statp = EX_NOHOST;
+ return FALSE;
+ }
+
+ /* Only want the first machine name */
+ if ((ptr = strchr(vptr, '\n')) != NULL)
+ *ptr = '\0';
+
+ if (hbsize >= strlen(vptr))
+ {
+ strcpy(name, vptr);
+ *statp = EX_OK;
+ return TRUE;
+ }
+ *statp = EX_UNAVAILABLE;
+ free(vptr);
+ return FALSE;
+}
+
+
+/*
+** NI_PROPVAL -- NetInfo property value lookup routine
+**
+** Parameters:
+** keydir -- the NetInfo directory name in which to search
+** for the key.
+** keyprop -- the name of the property in which to find the
+** property we are interested. Defaults to "name".
+** keyval -- the value for which we are really searching.
+** valprop -- the property name for the value in which we
+** are interested.
+** sepchar -- if non-nil, this can be multiple-valued, and
+** we should return a string separated by this
+** character.
+**
+** Returns:
+** NULL -- if:
+** 1. the directory is not found
+** 2. the property name is not found
+** 3. the property contains multiple values
+** 4. some error occured
+** else -- the value of the lookup.
+**
+** Example:
+** To search for an alias value, use:
+** ni_propval("/aliases", "name", aliasname, "members", ',')
+**
+** Notes:
+** Caller should free the return value of ni_proval
+*/
+
+# include <netinfo/ni.h>
+
+# define LOCAL_NETINFO_DOMAIN "."
+# define PARENT_NETINFO_DOMAIN ".."
+# define MAX_NI_LEVELS 256
+
+char *
+ni_propval(keydir, keyprop, keyval, valprop, sepchar)
+ char *keydir;
+ char *keyprop;
+ char *keyval;
+ char *valprop;
+ int sepchar;
+{
+ char *propval = NULL;
+ int i;
+ int j, alen;
+ void *ni = NULL;
+ void *lastni = NULL;
+ ni_status nis;
+ ni_id nid;
+ ni_namelist ninl;
+ register char *p;
+ char keybuf[1024];
+
+ /*
+ ** Create the full key from the two parts.
+ **
+ ** Note that directory can end with, e.g., "name=" to specify
+ ** an alternate search property.
+ */
+
+ i = strlen(keydir) + strlen(keyval) + 2;
+ if (keyprop != NULL)
+ i += strlen(keyprop) + 1;
+ if (i > sizeof keybuf)
+ return NULL;
+ strcpy(keybuf, keydir);
+ strcat(keybuf, "/");
+ if (keyprop != NULL)
+ {
+ strcat(keybuf, keyprop);
+ strcat(keybuf, "=");
+ }
+ strcat(keybuf, keyval);
+
+ if (tTd(38, 21))
+ printf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n",
+ keydir, keyprop, keyval, valprop, sepchar, keybuf);
+ /*
+ ** If the passed directory and property name are found
+ ** in one of netinfo domains we need to search (starting
+ ** from the local domain moving all the way back to the
+ ** root domain) set propval to the property's value
+ ** and return it.
+ */
+
+ for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++)
+ {
+ if (i == 0)
+ {
+ nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
+ if (tTd(38, 20))
+ printf("ni_open(LOCAL) = %d\n", nis);
+ }
+ else
+ {
+ if (lastni != NULL)
+ ni_free(lastni);
+ lastni = ni;
+ nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
+ if (tTd(38, 20))
+ printf("ni_open(PARENT) = %d\n", nis);
+ }
+
+ /*
+ ** Don't bother if we didn't get a handle on a
+ ** proper domain. This is not necessarily an error.
+ ** We would get a positive ni_status if, for instance
+ ** we never found the directory or property and tried
+ ** to open the parent of the root domain!
+ */
+
+ if (nis != 0)
+ break;
+
+ /*
+ ** Find the path to the server information.
+ */
+
+ if (ni_pathsearch(ni, &nid, keybuf) != 0)
+ continue;
+
+ /*
+ ** Find associated value information.
+ */
+
+ if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
+ continue;
+
+ if (tTd(38, 20))
+ printf("ni_lookupprop: len=%d\n", ninl.ni_namelist_len);
+ /*
+ ** See if we have an acceptable number of values.
+ */
+
+ if (ninl.ni_namelist_len <= 0)
+ continue;
+
+ if (sepchar == '\0' && ninl.ni_namelist_len > 1)
+ {
+ ni_namelist_free(&ninl);
+ continue;
+ }
+
+ /*
+ ** Calculate number of bytes needed and build result
+ */
+
+ alen = 1;
+ for (j = 0; j < ninl.ni_namelist_len; j++)
+ alen += strlen(ninl.ni_namelist_val[j]) + 1;
+ propval = p = xalloc(alen);
+ for (j = 0; j < ninl.ni_namelist_len; j++)
+ {
+ strcpy(p, ninl.ni_namelist_val[j]);
+ p += strlen(p);
+ *p++ = sepchar;
+ }
+ *--p = '\0';
+
+ ni_namelist_free(&ninl);
+ }
+
+ /*
+ ** Clean up.
+ */
+
+ if (ni != NULL)
+ ni_free(ni);
+ if (lastni != NULL && ni != lastni)
+ ni_free(lastni);
+ if (tTd(38, 20))
+ printf("ni_propval returns: '%s'\n", propval);
+
+ return propval;
+}
+
+#endif
+ /*
+** TEXT (unindexed text file) Modules
+**
+** This code donated by Sun Microsystems.
+*/
+
+#define map_sff map_lockfd /* overload field */
+
+
+/*
+** TEXT_MAP_OPEN -- open text table
+*/
+
+bool
+text_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ int sff;
+ int i;
+
+ if (tTd(38, 2))
+ printf("text_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+
+ mode &= O_ACCMODE;
+ if (mode != O_RDONLY)
+ {
+ errno = EPERM;
+ return FALSE;
+ }
+
+ if (*map->map_file == '\0')
+ {
+ syserr("text map \"%s\": file name required",
+ map->map_mname);
+ return FALSE;
+ }
+
+ if (map->map_file[0] != '/')
+ {
+ syserr("text map \"%s\": file name must be fully qualified",
+ map->map_mname);
+ return FALSE;
+ }
+
+ sff = SFF_ROOTOK|SFF_REGONLY;
+ if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
+ sff |= SFF_NOWLINK;
+ if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
+ sff |= SFF_SAFEDIRPATH;
+ if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName,
+ sff, S_IRUSR, NULL)) != 0)
+ {
+ /* cannot open this map */
+ if (tTd(38, 2))
+ printf("\tunsafe map file: %d\n", i);
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("text map \"%s\": unsafe map file %s",
+ map->map_mname, map->map_file);
+ return FALSE;
+ }
+
+ if (map->map_keycolnm == NULL)
+ map->map_keycolno = 0;
+ else
+ {
+ if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm)))
+ {
+ syserr("text map \"%s\", file %s: -k should specify a number, not %s",
+ map->map_mname, map->map_file,
+ map->map_keycolnm);
+ return FALSE;
+ }
+ map->map_keycolno = atoi(map->map_keycolnm);
+ }
+
+ if (map->map_valcolnm == NULL)
+ map->map_valcolno = 0;
+ else
+ {
+ if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm)))
+ {
+ syserr("text map \"%s\", file %s: -v should specify a number, not %s",
+ map->map_mname, map->map_file,
+ map->map_valcolnm);
+ return FALSE;
+ }
+ map->map_valcolno = atoi(map->map_valcolnm);
+ }
+
+ if (tTd(38, 2))
+ {
+ printf("text_map_open(%s, %s): delimiter = ",
+ map->map_mname, map->map_file);
+ if (map->map_coldelim == '\0')
+ printf("(white space)\n");
+ else
+ printf("%c\n", map->map_coldelim);
+ }
+
+ map->map_sff = sff;
+ return TRUE;
+}
+
+
+/*
+** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
+*/
+
+char *
+text_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ char *vp;
+ auto int vsize;
+ int buflen;
+ FILE *f;
+ char delim;
+ int key_idx;
+ bool found_it;
+ int sff = map->map_sff;
+ char search_key[MAXNAME + 1];
+ char linebuf[MAXLINE];
+ char buf[MAXNAME + 1];
+ extern char *get_column __P((char *, int, char, char *, int));
+
+ found_it = FALSE;
+ if (tTd(38, 20))
+ printf("text_map_lookup(%s, %s)\n", map->map_mname, name);
+
+ buflen = strlen(name);
+ if (buflen > sizeof search_key - 1)
+ buflen = sizeof search_key - 1;
+ bcopy(name, search_key, buflen);
+ search_key[buflen] = '\0';
+ if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+ makelower(search_key);
+
+ f = safefopen(map->map_file, O_RDONLY, FileMode, sff);
+ if (f == NULL)
+ {
+ map->map_mflags &= ~(MF_VALID|MF_OPEN);
+ *statp = EX_UNAVAILABLE;
+ return NULL;
+ }
+ key_idx = map->map_keycolno;
+ delim = map->map_coldelim;
+ while (fgets(linebuf, MAXLINE, f) != NULL)
+ {
+ char *p;
+
+ /* skip comment line */
+ if (linebuf[0] == '#')
+ continue;
+ p = strchr(linebuf, '\n');
+ if (p != NULL)
+ *p = '\0';
+ p = get_column(linebuf, key_idx, delim, buf, sizeof buf);
+ if (p != NULL && strcasecmp(search_key, p) == 0)
+ {
+ found_it = TRUE;
+ break;
+ }
+ }
+ fclose(f);
+ if (!found_it)
+ {
+ *statp = EX_NOTFOUND;
+ return NULL;
+ }
+ vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf);
+ vsize = strlen(vp);
+ *statp = EX_OK;
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ return map_rewrite(map, name, strlen(name), NULL);
+ else
+ return map_rewrite(map, vp, vsize, av);
+}
+
+
+/*
+** TEXT_GETCANONNAME -- look up canonical name in hosts file
+*/
+
+bool
+text_getcanonname(name, hbsize, statp)
+ char *name;
+ int hbsize;
+ int *statp;
+{
+ bool found;
+ FILE *f;
+ char linebuf[MAXLINE];
+ char cbuf[MAXNAME + 1];
+ char nbuf[MAXNAME + 1];
+
+ if (tTd(38, 20))
+ printf("text_getcanonname(%s)\n", name);
+
+ if (strlen(name) >= (SIZE_T) sizeof nbuf)
+ {
+ *statp = EX_UNAVAILABLE;
+ return FALSE;
+ }
+ (void) strcpy(nbuf, name);
+ shorten_hostname(nbuf);
+
+ f = fopen(HostsFile, "r");
+ if (f == NULL)
+ {
+ *statp = EX_UNAVAILABLE;
+ return FALSE;
+ }
+ found = FALSE;
+ while (!found && fgets(linebuf, MAXLINE, f) != NULL)
+ {
+ char *p = strpbrk(linebuf, "#\n");
+
+ if (p != NULL)
+ *p = '\0';
+ if (linebuf[0] != '\0')
+ found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf);
+ }
+ fclose(f);
+ if (!found)
+ {
+ *statp = EX_NOHOST;
+ return FALSE;
+ }
+
+ if ((SIZE_T) hbsize >= strlen(cbuf))
+ {
+ strcpy(name, cbuf);
+ *statp = EX_OK;
+ return TRUE;
+ }
+ *statp = EX_UNAVAILABLE;
+ return FALSE;
+}
+ /*
+** STAB (Symbol Table) Modules
+*/
+
+
+/*
+** STAB_MAP_LOOKUP -- look up alias in symbol table
+*/
+
+/* ARGSUSED2 */
+char *
+stab_map_lookup(map, name, av, pstat)
+ register MAP *map;
+ char *name;
+ char **av;
+ int *pstat;
+{
+ register STAB *s;
+
+ if (tTd(38, 20))
+ printf("stab_lookup(%s, %s)\n",
+ map->map_mname, name);
+
+ s = stab(name, ST_ALIAS, ST_FIND);
+ if (s != NULL)
+ return (s->s_alias);
+ return (NULL);
+}
+
+
+/*
+** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
+*/
+
+void
+stab_map_store(map, lhs, rhs)
+ register MAP *map;
+ char *lhs;
+ char *rhs;
+{
+ register STAB *s;
+
+ s = stab(lhs, ST_ALIAS, ST_ENTER);
+ s->s_alias = newstr(rhs);
+}
+
+
+/*
+** STAB_MAP_OPEN -- initialize (reads data file)
+**
+** This is a wierd case -- it is only intended as a fallback for
+** aliases. For this reason, opens for write (only during a
+** "newaliases") always fails, and opens for read open the
+** actual underlying text file instead of the database.
+*/
+
+bool
+stab_map_open(map, mode)
+ register MAP *map;
+ int mode;
+{
+ FILE *af;
+ int sff;
+ struct stat st;
+
+ if (tTd(38, 2))
+ printf("stab_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+
+ mode &= O_ACCMODE;
+ if (mode != O_RDONLY)
+ {
+ errno = EPERM;
+ return FALSE;
+ }
+
+ sff = SFF_ROOTOK|SFF_REGONLY;
+ if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
+ sff |= SFF_NOWLINK;
+ if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail))
+ sff |= SFF_SAFEDIRPATH;
+ af = safefopen(map->map_file, O_RDONLY, 0444, sff);
+ if (af == NULL)
+ return FALSE;
+ readaliases(map, af, FALSE, FALSE);
+
+ if (fstat(fileno(af), &st) >= 0)
+ map->map_mtime = st.st_mtime;
+ fclose(af);
+
+ return TRUE;
+}
+ /*
+** Implicit Modules
+**
+** Tries several types. For back compatibility of aliases.
+*/
+
+
+/*
+** IMPL_MAP_LOOKUP -- lookup in best open database
+*/
+
+char *
+impl_map_lookup(map, name, av, pstat)
+ MAP *map;
+ char *name;
+ char **av;
+ int *pstat;
+{
+ if (tTd(38, 20))
+ printf("impl_map_lookup(%s, %s)\n",
+ map->map_mname, name);
+
+#ifdef NEWDB
+ if (bitset(MF_IMPL_HASH, map->map_mflags))
+ return db_map_lookup(map, name, av, pstat);
+#endif
+#ifdef NDBM
+ if (bitset(MF_IMPL_NDBM, map->map_mflags))
+ return ndbm_map_lookup(map, name, av, pstat);
+#endif
+ return stab_map_lookup(map, name, av, pstat);
+}
+
+/*
+** IMPL_MAP_STORE -- store in open databases
+*/
+
+void
+impl_map_store(map, lhs, rhs)
+ MAP *map;
+ char *lhs;
+ char *rhs;
+{
+ if (tTd(38, 12))
+ printf("impl_map_store(%s, %s, %s)\n",
+ map->map_mname, lhs, rhs);
+#ifdef NEWDB
+ if (bitset(MF_IMPL_HASH, map->map_mflags))
+ db_map_store(map, lhs, rhs);
+#endif
+#ifdef NDBM
+ if (bitset(MF_IMPL_NDBM, map->map_mflags))
+ ndbm_map_store(map, lhs, rhs);
+#endif
+ stab_map_store(map, lhs, rhs);
+}
+
+/*
+** IMPL_MAP_OPEN -- implicit database open
+*/
+
+bool
+impl_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ if (tTd(38, 2))
+ printf("impl_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+
+ mode &= O_ACCMODE;
+#ifdef NEWDB
+ map->map_mflags |= MF_IMPL_HASH;
+ if (hash_map_open(map, mode))
+ {
+# ifdef NDBM_YP_COMPAT
+ if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
+# endif
+ return TRUE;
+ }
+ else
+ map->map_mflags &= ~MF_IMPL_HASH;
+#endif
+#ifdef NDBM
+ map->map_mflags |= MF_IMPL_NDBM;
+ if (ndbm_map_open(map, mode))
+ {
+ return TRUE;
+ }
+ else
+ map->map_mflags &= ~MF_IMPL_NDBM;
+#endif
+
+#if defined(NEWDB) || defined(NDBM)
+ if (Verbose)
+ message("WARNING: cannot open alias database %s%s",
+ map->map_file,
+ mode == O_RDONLY ? "; reading text version" : "");
+#else
+ if (mode != O_RDONLY)
+ usrerr("Cannot rebuild aliases: no database format defined");
+#endif
+
+ if (mode == O_RDONLY)
+ return stab_map_open(map, mode);
+ else
+ return FALSE;
+}
+
+
+/*
+** IMPL_MAP_CLOSE -- close any open database(s)
+*/
+
+void
+impl_map_close(map)
+ MAP *map;
+{
+ if (tTd(38, 9))
+ printf("impl_map_close(%s, %s, %lx)\n",
+ map->map_mname, map->map_file, map->map_mflags);
+#ifdef NEWDB
+ if (bitset(MF_IMPL_HASH, map->map_mflags))
+ {
+ db_map_close(map);
+ map->map_mflags &= ~MF_IMPL_HASH;
+ }
+#endif
+
+#ifdef NDBM
+ if (bitset(MF_IMPL_NDBM, map->map_mflags))
+ {
+ ndbm_map_close(map);
+ map->map_mflags &= ~MF_IMPL_NDBM;
+ }
+#endif
+}
+ /*
+** User map class.
+**
+** Provides access to the system password file.
+*/
+
+/*
+** USER_MAP_OPEN -- open user map
+**
+** Really just binds field names to field numbers.
+*/
+
+bool
+user_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ if (tTd(38, 2))
+ printf("user_map_open(%s, %d)\n",
+ map->map_mname, mode);
+
+ mode &= O_ACCMODE;
+ if (mode != O_RDONLY)
+ {
+ /* issue a pseudo-error message */
+#ifdef ENOSYS
+ errno = ENOSYS;
+#else
+# ifdef EFTYPE
+ errno = EFTYPE;
+# else
+ errno = ENXIO;
+# endif
+#endif
+ return FALSE;
+ }
+ if (map->map_valcolnm == NULL)
+ /* nothing */ ;
+ else if (strcasecmp(map->map_valcolnm, "name") == 0)
+ map->map_valcolno = 1;
+ else if (strcasecmp(map->map_valcolnm, "passwd") == 0)
+ map->map_valcolno = 2;
+ else if (strcasecmp(map->map_valcolnm, "uid") == 0)
+ map->map_valcolno = 3;
+ else if (strcasecmp(map->map_valcolnm, "gid") == 0)
+ map->map_valcolno = 4;
+ else if (strcasecmp(map->map_valcolnm, "gecos") == 0)
+ map->map_valcolno = 5;
+ else if (strcasecmp(map->map_valcolnm, "dir") == 0)
+ map->map_valcolno = 6;
+ else if (strcasecmp(map->map_valcolnm, "shell") == 0)
+ map->map_valcolno = 7;
+ else
+ {
+ syserr("User map %s: unknown column name %s",
+ map->map_mname, map->map_valcolnm);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+** USER_MAP_LOOKUP -- look up a user in the passwd file.
+*/
+
+/* ARGSUSED3 */
+char *
+user_map_lookup(map, key, av, statp)
+ MAP *map;
+ char *key;
+ char **av;
+ int *statp;
+{
+ struct passwd *pw;
+ auto bool fuzzy;
+
+ if (tTd(38, 20))
+ printf("user_map_lookup(%s, %s)\n",
+ map->map_mname, key);
+
+ pw = finduser(key, &fuzzy);
+ if (pw == NULL)
+ return NULL;
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ return map_rewrite(map, key, strlen(key), NULL);
+ else
+ {
+ char *rwval = NULL;
+ char buf[30];
+
+ switch (map->map_valcolno)
+ {
+ case 0:
+ case 1:
+ rwval = pw->pw_name;
+ break;
+
+ case 2:
+ rwval = pw->pw_passwd;
+ break;
+
+ case 3:
+ snprintf(buf, sizeof buf, "%d", pw->pw_uid);
+ rwval = buf;
+ break;
+
+ case 4:
+ snprintf(buf, sizeof buf, "%d", pw->pw_gid);
+ rwval = buf;
+ break;
+
+ case 5:
+ rwval = pw->pw_gecos;
+ break;
+
+ case 6:
+ rwval = pw->pw_dir;
+ break;
+
+ case 7:
+ rwval = pw->pw_shell;
+ break;
+ }
+ return map_rewrite(map, rwval, strlen(rwval), av);
+ }
+}
+ /*
+** Program map type.
+**
+** This provides access to arbitrary programs. It should be used
+** only very sparingly, since there is no way to bound the cost
+** of invoking an arbitrary program.
+*/
+
+char *
+prog_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ int i;
+ register char *p;
+ int fd;
+ auto pid_t pid;
+ char *rval;
+ int stat;
+ char *argv[MAXPV + 1];
+ char buf[MAXLINE];
+
+ if (tTd(38, 20))
+ printf("prog_map_lookup(%s, %s) %s\n",
+ map->map_mname, name, map->map_file);
+
+ i = 0;
+ argv[i++] = map->map_file;
+ if (map->map_rebuild != NULL)
+ {
+ snprintf(buf, sizeof buf, "%s", map->map_rebuild);
+ for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t"))
+ {
+ if (i >= MAXPV - 1)
+ break;
+ argv[i++] = p;
+ }
+ }
+ argv[i++] = name;
+ argv[i] = NULL;
+ if (tTd(38, 21))
+ {
+ printf("prog_open:");
+ for (i = 0; argv[i] != NULL; i++)
+ printf(" %s", argv[i]);
+ printf("\n");
+ }
+ (void) blocksignal(SIGCHLD);
+ pid = prog_open(argv, &fd, CurEnv);
+ if (pid < 0)
+ {
+ if (!bitset(MF_OPTIONAL, map->map_mflags))
+ syserr("prog_map_lookup(%s) failed (%s) -- closing",
+ map->map_mname, errstring(errno));
+ else if (tTd(38, 9))
+ printf("prog_map_lookup(%s) failed (%s) -- closing",
+ map->map_mname, errstring(errno));
+ map->map_mflags &= ~(MF_VALID|MF_OPEN);
+ *statp = EX_OSFILE;
+ return NULL;
+ }
+ i = read(fd, buf, sizeof buf - 1);
+ if (i < 0)
+ {
+ syserr("prog_map_lookup(%s): read error %s\n",
+ map->map_mname, errstring(errno));
+ rval = NULL;
+ }
+ else if (i == 0)
+ {
+ if (tTd(38, 20))
+ printf("prog_map_lookup(%s): empty answer\n",
+ map->map_mname);
+ rval = NULL;
+ }
+ else
+ {
+ buf[i] = '\0';
+ p = strchr(buf, '\n');
+ if (p != NULL)
+ *p = '\0';
+
+ /* collect the return value */
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ rval = map_rewrite(map, name, strlen(name), NULL);
+ else
+ rval = map_rewrite(map, buf, strlen(buf), NULL);
+
+ /* now flush any additional output */
+ while ((i = read(fd, buf, sizeof buf)) > 0)
+ continue;
+ }
+
+ /* wait for the process to terminate */
+ close(fd);
+ stat = waitfor(pid);
+ (void) releasesignal(SIGCHLD);
+
+ if (stat == -1)
+ {
+ syserr("prog_map_lookup(%s): wait error %s\n",
+ map->map_mname, errstring(errno));
+ *statp = EX_SOFTWARE;
+ rval = NULL;
+ }
+ else if (WIFEXITED(stat))
+ {
+ if ((*statp = WEXITSTATUS(stat)) != EX_OK)
+ rval = NULL;
+ }
+ else
+ {
+ syserr("prog_map_lookup(%s): child died on signal %d",
+ map->map_mname, stat);
+ *statp = EX_UNAVAILABLE;
+ rval = NULL;
+ }
+ return rval;
+}
+ /*
+** Sequenced map type.
+**
+** Tries each map in order until something matches, much like
+** implicit. Stores go to the first map in the list that can
+** support storing.
+**
+** This is slightly unusual in that there are two interfaces.
+** The "sequence" interface lets you stack maps arbitrarily.
+** The "switch" interface builds a sequence map by looking
+** at a system-dependent configuration file such as
+** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
+**
+** We don't need an explicit open, since all maps are
+** opened during startup, including underlying maps.
+*/
+
+/*
+** SEQ_MAP_PARSE -- Sequenced map parsing
+*/
+
+bool
+seq_map_parse(map, ap)
+ MAP *map;
+ char *ap;
+{
+ int maxmap;
+
+ if (tTd(38, 2))
+ printf("seq_map_parse(%s, %s)\n", map->map_mname, ap);
+ maxmap = 0;
+ while (*ap != '\0')
+ {
+ register char *p;
+ STAB *s;
+
+ /* find beginning of map name */
+ while (isascii(*ap) && isspace(*ap))
+ ap++;
+ for (p = ap; isascii(*p) && isalnum(*p); p++)
+ continue;
+ if (*p != '\0')
+ *p++ = '\0';
+ while (*p != '\0' && (!isascii(*p) || !isalnum(*p)))
+ p++;
+ if (*ap == '\0')
+ {
+ ap = p;
+ continue;
+ }
+ s = stab(ap, ST_MAP, ST_FIND);
+ if (s == NULL)
+ {
+ syserr("Sequence map %s: unknown member map %s",
+ map->map_mname, ap);
+ }
+ else if (maxmap == MAXMAPSTACK)
+ {
+ syserr("Sequence map %s: too many member maps (%d max)",
+ map->map_mname, MAXMAPSTACK);
+ maxmap++;
+ }
+ else if (maxmap < MAXMAPSTACK)
+ {
+ map->map_stack[maxmap++] = &s->s_map;
+ }
+ ap = p;
+ }
+ return TRUE;
+}
+
+
+/*
+** SWITCH_MAP_OPEN -- open a switched map
+**
+** This looks at the system-dependent configuration and builds
+** a sequence map that does the same thing.
+**
+** Every system must define a switch_map_find routine in conf.c
+** that will return the list of service types associated with a
+** given service class.
+*/
+
+bool
+switch_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ int mapno;
+ int nmaps;
+ char *maptype[MAXMAPSTACK];
+
+ if (tTd(38, 2))
+ printf("switch_map_open(%s, %s, %d)\n",
+ map->map_mname, map->map_file, mode);
+
+ mode &= O_ACCMODE;
+ nmaps = switch_map_find(map->map_file, maptype, map->map_return);
+ if (tTd(38, 19))
+ {
+ printf("\tswitch_map_find => %d\n", nmaps);
+ for (mapno = 0; mapno < nmaps; mapno++)
+ printf("\t\t%s\n", maptype[mapno]);
+ }
+ if (nmaps <= 0 || nmaps > MAXMAPSTACK)
+ return FALSE;
+
+ for (mapno = 0; mapno < nmaps; mapno++)
+ {
+ register STAB *s;
+ char nbuf[MAXNAME + 1];
+
+ if (maptype[mapno] == NULL)
+ continue;
+ (void) snprintf(nbuf, sizeof nbuf, "%s.%s",
+ map->map_mname, maptype[mapno]);
+ s = stab(nbuf, ST_MAP, ST_FIND);
+ if (s == NULL)
+ {
+ syserr("Switch map %s: unknown member map %s",
+ map->map_mname, nbuf);
+ }
+ else
+ {
+ map->map_stack[mapno] = &s->s_map;
+ if (tTd(38, 4))
+ printf("\tmap_stack[%d] = %s:%s\n",
+ mapno, s->s_map.map_class->map_cname,
+ nbuf);
+ }
+ }
+ return TRUE;
+}
+
+
+/*
+** SEQ_MAP_CLOSE -- close all underlying maps
+*/
+
+void
+seq_map_close(map)
+ MAP *map;
+{
+ int mapno;
+
+ if (tTd(38, 9))
+ printf("seq_map_close(%s)\n", map->map_mname);
+
+ for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
+ {
+ MAP *mm = map->map_stack[mapno];
+
+ if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags))
+ continue;
+ mm->map_class->map_close(mm);
+ mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
+ }
+}
+
+
+/*
+** SEQ_MAP_LOOKUP -- sequenced map lookup
+*/
+
+char *
+seq_map_lookup(map, key, args, pstat)
+ MAP *map;
+ char *key;
+ char **args;
+ int *pstat;
+{
+ int mapno;
+ int mapbit = 0x01;
+ bool tempfail = FALSE;
+
+ if (tTd(38, 20))
+ printf("seq_map_lookup(%s, %s)\n", map->map_mname, key);
+
+ for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++)
+ {
+ MAP *mm = map->map_stack[mapno];
+ char *rv;
+
+ if (mm == NULL)
+ continue;
+ if (!bitset(MF_OPEN, mm->map_mflags))
+ {
+ if (bitset(mapbit, map->map_return[MA_UNAVAIL]))
+ {
+ *pstat = EX_UNAVAILABLE;
+ return NULL;
+ }
+ continue;
+ }
+ *pstat = EX_OK;
+ rv = mm->map_class->map_lookup(mm, key, args, pstat);
+ if (rv != NULL)
+ return rv;
+ if (*pstat == EX_TEMPFAIL)
+ {
+ if (bitset(mapbit, map->map_return[MA_TRYAGAIN]))
+ return NULL;
+ tempfail = TRUE;
+ }
+ else if (bitset(mapbit, map->map_return[MA_NOTFOUND]))
+ break;
+ }
+ if (tempfail)
+ *pstat = EX_TEMPFAIL;
+ else if (*pstat == EX_OK)
+ *pstat = EX_NOTFOUND;
+ return NULL;
+}
+
+
+/*
+** SEQ_MAP_STORE -- sequenced map store
+*/
+
+void
+seq_map_store(map, key, val)
+ MAP *map;
+ char *key;
+ char *val;
+{
+ int mapno;
+
+ if (tTd(38, 12))
+ printf("seq_map_store(%s, %s, %s)\n",
+ map->map_mname, key, val);
+
+ for (mapno = 0; mapno < MAXMAPSTACK; mapno++)
+ {
+ MAP *mm = map->map_stack[mapno];
+
+ if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags))
+ continue;
+
+ mm->map_class->map_store(mm, key, val);
+ return;
+ }
+ syserr("seq_map_store(%s, %s, %s): no writable map",
+ map->map_mname, key, val);
+}
+ /*
+** NULL stubs
+*/
+
+/* ARGSUSED */
+bool
+null_map_open(map, mode)
+ MAP *map;
+ int mode;
+{
+ return TRUE;
+}
+
+/* ARGSUSED */
+void
+null_map_close(map)
+ MAP *map;
+{
+ return;
+}
+
+char *
+null_map_lookup(map, key, args, pstat)
+ MAP *map;
+ char *key;
+ char **args;
+ int *pstat;
+{
+ *pstat = EX_NOTFOUND;
+ return NULL;
+}
+
+/* ARGSUSED */
+void
+null_map_store(map, key, val)
+ MAP *map;
+ char *key;
+ char *val;
+{
+ return;
+}
+
+
+/*
+** BOGUS stubs
+*/
+
+char *
+bogus_map_lookup(map, key, args, pstat)
+ MAP *map;
+ char *key;
+ char **args;
+ int *pstat;
+{
+ *pstat = EX_TEMPFAIL;
+ return NULL;
+}
+
+MAPCLASS BogusMapClass =
+{
+ "bogus-map", NULL, 0,
+ NULL, bogus_map_lookup, null_map_store,
+ null_map_open, null_map_close,
+};
+ /*
+** REGEX modules
+*/
+
+#ifdef MAP_REGEX
+
+# include <regex.h>
+
+# define DEFAULT_DELIM CONDELSE
+
+# define END_OF_FIELDS -1
+
+# define ERRBUF_SIZE 80
+# define MAX_MATCH 32
+
+# define xnalloc(s) memset(xalloc(s), 0, s);
+
+struct regex_map
+{
+ regex_t pattern_buf; /* xalloc it */
+ int *regex_subfields; /* move to type MAP */
+ char *delim; /* move to type MAP */
+};
+
+static int
+parse_fields(s, ibuf, blen, nr_substrings)
+ char *s;
+ int *ibuf; /* array */
+ int blen; /* number of elements in ibuf */
+ int nr_substrings; /* number of substrings in the pattern */
+{
+ register char *cp;
+ int i = 0;
+ bool lastone = FALSE;
+
+ blen--; /* for terminating END_OF_FIELDS */
+ cp = s;
+ do
+ {
+ for (;; cp++)
+ {
+ if (*cp == ',')
+ {
+ *cp = '\0';
+ break;
+ }
+ if (*cp == '\0')
+ {
+ lastone = TRUE;
+ break;
+ }
+ }
+ if (i < blen)
+ {
+ int val = atoi(s);
+
+ if (val < 0 || val >= nr_substrings)
+ {
+ syserr("field (%d) out of range, only %d substrings in pattern",
+ val, nr_substrings);
+ return -1;
+ }
+ ibuf[i++] = val;
+ }
+ else
+ {
+ syserr("too many fields, %d max\n", blen);
+ return -1;
+ }
+ s = ++cp;
+ } while (!lastone);
+ ibuf[i] = END_OF_FIELDS;
+ return i;
+}
+
+bool
+regex_map_init(map, ap)
+ MAP *map;
+ char *ap;
+{
+ int regerr;
+ struct regex_map *map_p;
+ register char *p;
+ char *sub_param = NULL;
+ int pflags;
+ static char defdstr[] = { (char)DEFAULT_DELIM, '\0' };
+
+ if (tTd(38, 2))
+ printf("regex_map_init: mapname '%s', args '%s'\n",
+ map->map_mname, ap);
+
+ pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB;
+
+ p = ap;
+
+ map_p = (struct regex_map *) xnalloc(sizeof(struct regex_map));
+
+ for (;;)
+ {
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p != '-')
+ break;
+ switch (*++p)
+ {
+ case 'n': /* not */
+ map->map_mflags |= MF_REGEX_NOT;
+ break;
+
+ case 'f': /* case sensitive */
+ map->map_mflags |= MF_NOFOLDCASE;
+ pflags &= ~REG_ICASE;
+ break;
+
+ case 'b': /* basic regular expressions */
+ pflags &= ~REG_EXTENDED;
+ break;
+
+ case 's': /* substring match () syntax */
+ sub_param = ++p;
+ pflags &= ~REG_NOSUB;
+ break;
+
+ case 'd': /* delimiter */
+ map_p->delim = ++p;
+ break;
+
+ case 'a': /* map append */
+ map->map_app = ++p;
+ break;
+
+ case 'm': /* matchonly */
+ map->map_mflags |= MF_MATCHONLY;
+ break;
+
+ }
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+ if (tTd(38, 3))
+ printf("regex_map_init: compile '%s' 0x%x\n", p, pflags);
+
+ if ((regerr = regcomp(&(map_p->pattern_buf), p, pflags)) != 0)
+ {
+ /* Errorhandling */
+ char errbuf[ERRBUF_SIZE];
+
+ regerror(regerr, &(map_p->pattern_buf), errbuf, ERRBUF_SIZE);
+ syserr("pattern-compile-error: %s\n", errbuf);
+ free(map_p);
+ return FALSE;
+ }
+
+ if (map->map_app != NULL)
+ map->map_app = newstr(map->map_app);
+ if (map_p->delim != NULL)
+ map_p->delim = newstr(map_p->delim);
+ else
+ map_p->delim = defdstr;
+
+ if (!bitset(REG_NOSUB, pflags))
+ {
+ /* substring matching */
+ int substrings;
+ int *fields = (int *)xalloc(sizeof(int) * (MAX_MATCH + 1));
+
+ substrings = map_p->pattern_buf.re_nsub + 1;
+
+ if (tTd(38, 3))
+ printf("regex_map_init: nr of substrings %d\n", substrings);
+
+ if (substrings >= MAX_MATCH)
+ {
+ syserr("too many substrings, %d max\n", MAX_MATCH);
+ free(map_p);
+ return FALSE;
+ }
+ if (sub_param != NULL && sub_param[0] != '\0')
+ {
+ /* optional parameter -sfields */
+ if (parse_fields(sub_param, fields,
+ MAX_MATCH + 1, substrings) == -1)
+ return FALSE;
+ }
+ else
+ {
+ /* set default fields */
+ int i;
+
+ for (i = 0; i < substrings; i++)
+ fields[i] = i;
+ fields[i] = END_OF_FIELDS;
+ }
+ map_p->regex_subfields = fields;
+ if (tTd(38, 3))
+ {
+ int *ip;
+
+ printf("regex_map_init: subfields");
+ for (ip = fields; *ip != END_OF_FIELDS; ip++)
+ printf(" %d", *ip);
+ printf("\n");
+ }
+ }
+ map->map_db1 = (ARBPTR_T)map_p; /* dirty hack */
+
+ return TRUE;
+}
+
+static char *
+regex_map_rewrite(map, s, slen, av)
+ MAP *map;
+ const char *s;
+ size_t slen;
+ char **av;
+{
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ return map_rewrite(map, av[0], strlen(av[0]), NULL);
+ else
+ return map_rewrite(map, s, slen, NULL);
+}
+
+char *
+regex_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ int reg_res;
+ struct regex_map *map_p;
+ regmatch_t pmatch[MAX_MATCH];
+
+ if (tTd(38, 20))
+ {
+ char **cpp;
+
+ printf("regex_map_lookup: key '%s'\n", name);
+ for (cpp = av; cpp && *cpp; cpp++)
+ printf("regex_map_lookup: arg '%s'\n", *cpp);
+ }
+
+ map_p = (struct regex_map *)(map->map_db1);
+ reg_res = regexec(&(map_p->pattern_buf), name, MAX_MATCH, pmatch, 0);
+
+ if (bitset(MF_REGEX_NOT, map->map_mflags))
+ {
+ /* option -n */
+ if (reg_res == REG_NOMATCH)
+ return regex_map_rewrite(map, "", (size_t)0, av);
+ else
+ return NULL;
+ }
+ if (reg_res == REG_NOMATCH)
+ return NULL;
+
+ if (map_p->regex_subfields != NULL)
+ {
+ /* option -s */
+ static char retbuf[MAXNAME];
+ int fields[MAX_MATCH + 1];
+ bool first = TRUE;
+ int anglecnt = 0, cmntcnt = 0, spacecnt = 0;
+ bool quotemode = FALSE, bslashmode = FALSE;
+ register char *dp, *sp;
+ char *endp, *ldp;
+ int *ip;
+
+ dp = retbuf;
+ ldp = retbuf + sizeof(retbuf) - 1;
+
+ if (av[1] != NULL)
+ {
+ if (parse_fields(av[1], fields, MAX_MATCH + 1,
+ (int) map_p->pattern_buf.re_nsub + 1) == -1)
+ {
+ *statp = EX_CONFIG;
+ return NULL;
+ }
+ ip = fields;
+ }
+ else
+ ip = map_p->regex_subfields;
+
+ for ( ; *ip != END_OF_FIELDS; ip++)
+ {
+ if (!first)
+ {
+ for (sp = map_p->delim; *sp; sp++)
+ {
+ if (dp < ldp)
+ *dp++ = *sp;
+ }
+ }
+ else
+ first = FALSE;
+
+
+ if (pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0)
+ continue;
+
+ sp = name + pmatch[*ip].rm_so;
+ endp = name + pmatch[*ip].rm_eo;
+ for (; endp > sp; sp++)
+ {
+ if (dp < ldp)
+ {
+ if(bslashmode)
+ {
+ *dp++ = *sp;
+ bslashmode = FALSE;
+ }
+ else if(quotemode && *sp != '"' &&
+ *sp != '\\')
+ {
+ *dp++ = *sp;
+ }
+ else switch(*dp++ = *sp)
+ {
+ case '\\':
+ bslashmode = TRUE;
+ break;
+
+ case '(':
+ cmntcnt++;
+ break;
+
+ case ')':
+ cmntcnt--;
+ break;
+
+ case '<':
+ anglecnt++;
+ break;
+
+ case '>':
+ anglecnt--;
+ break;
+
+ case ' ':
+ spacecnt++;
+ break;
+
+ case '"':
+ quotemode = !quotemode;
+ break;
+ }
+ }
+ }
+ }
+ if (anglecnt != 0 || cmntcnt != 0 || quotemode ||
+ bslashmode || spacecnt != 0)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "Warning: regex may cause prescan() failure map=%s lookup=%s",
+ map->map_mname, name);
+ return NULL;
+ }
+
+ *dp = '\0';
+
+ return regex_map_rewrite(map, retbuf, strlen(retbuf), av);
+ }
+ return regex_map_rewrite(map, "", (size_t)0, av);
+}
+#endif /* MAP_REGEX */
diff --git a/contrib/sendmail/src/mci.c b/contrib/sendmail/src/mci.c
new file mode 100644
index 0000000..efb8cba
--- /dev/null
+++ b/contrib/sendmail/src/mci.c
@@ -0,0 +1,1293 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mci.c 8.82 (Berkeley) 6/15/98";
+#endif /* not lint */
+
+#include "sendmail.h"
+#include <arpa/inet.h>
+#include <dirent.h>
+
+/*
+** Mail Connection Information (MCI) Caching Module.
+**
+** There are actually two separate things cached. The first is
+** the set of all open connections -- these are stored in a
+** (small) list. The second is stored in the symbol table; it
+** has the overall status for all hosts, whether or not there
+** is a connection open currently.
+**
+** There should never be too many connections open (since this
+** could flood the socket table), nor should a connection be
+** allowed to sit idly for too long.
+**
+** MaxMciCache is the maximum number of open connections that
+** will be supported.
+**
+** MciCacheTimeout is the time (in seconds) that a connection
+** is permitted to survive without activity.
+**
+** We actually try any cached connections by sending a NOOP
+** before we use them; if the NOOP fails we close down the
+** connection and reopen it. Note that this means that a
+** server SMTP that doesn't support NOOP will hose the
+** algorithm -- but that doesn't seem too likely.
+**
+** The persistent MCI code is donated by Mark Lovell and Paul
+** Vixie. It is based on the long term host status code in KJS
+** written by Paul but has been adapted by Mark to fit into the
+** MCI structure.
+*/
+
+MCI **MciCache; /* the open connection cache */
+
+extern int mci_generate_persistent_path __P((const char *, char *, int, bool));
+extern bool mci_load_persistent __P((MCI *));
+extern void mci_uncache __P((MCI **, bool));
+ /*
+** MCI_CACHE -- enter a connection structure into the open connection cache
+**
+** This may cause something else to be flushed.
+**
+** Parameters:
+** mci -- the connection to cache.
+**
+** Returns:
+** none.
+*/
+
+void
+mci_cache(mci)
+ register MCI *mci;
+{
+ register MCI **mcislot;
+
+ /*
+ ** Find the best slot. This may cause expired connections
+ ** to be closed.
+ */
+
+ mcislot = mci_scan(mci);
+ if (mcislot == NULL)
+ {
+ /* we don't support caching */
+ return;
+ }
+
+ if (mci->mci_host == NULL)
+ return;
+
+ /* if this is already cached, we are done */
+ if (bitset(MCIF_CACHED, mci->mci_flags))
+ return;
+
+ /* otherwise we may have to clear the slot */
+ if (*mcislot != NULL)
+ mci_uncache(mcislot, TRUE);
+
+ if (tTd(42, 5))
+ printf("mci_cache: caching %lx (%s) in slot %d\n",
+ (u_long) mci, mci->mci_host, (int)(mcislot - MciCache));
+ if (tTd(91, 100))
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "mci_cache: caching %x (%.100s) in slot %d",
+ mci, mci->mci_host, mcislot - MciCache);
+
+ *mcislot = mci;
+ mci->mci_flags |= MCIF_CACHED;
+}
+ /*
+** MCI_SCAN -- scan the cache, flush junk, and return best slot
+**
+** Parameters:
+** savemci -- never flush this one. Can be null.
+**
+** Returns:
+** The LRU (or empty) slot.
+*/
+
+MCI **
+mci_scan(savemci)
+ MCI *savemci;
+{
+ time_t now;
+ register MCI **bestmci;
+ register MCI *mci;
+ register int i;
+
+ if (MaxMciCache <= 0)
+ {
+ /* we don't support caching */
+ return NULL;
+ }
+
+ if (MciCache == NULL)
+ {
+ /* first call */
+ MciCache = (MCI **) xalloc(MaxMciCache * sizeof *MciCache);
+ bzero((char *) MciCache, MaxMciCache * sizeof *MciCache);
+ return (&MciCache[0]);
+ }
+
+ now = curtime();
+ bestmci = &MciCache[0];
+ for (i = 0; i < MaxMciCache; i++)
+ {
+ mci = MciCache[i];
+ if (mci == NULL || mci->mci_state == MCIS_CLOSED)
+ {
+ bestmci = &MciCache[i];
+ continue;
+ }
+ if (mci->mci_lastuse + MciCacheTimeout < now && mci != savemci)
+ {
+ /* connection idle too long -- close it */
+ bestmci = &MciCache[i];
+ mci_uncache(bestmci, TRUE);
+ continue;
+ }
+ if (*bestmci == NULL)
+ continue;
+ if (mci->mci_lastuse < (*bestmci)->mci_lastuse)
+ bestmci = &MciCache[i];
+ }
+ return bestmci;
+}
+ /*
+** MCI_UNCACHE -- remove a connection from a slot.
+**
+** May close a connection.
+**
+** Parameters:
+** mcislot -- the slot to empty.
+** doquit -- if TRUE, send QUIT protocol on this connection.
+** if FALSE, we are assumed to be in a forked child;
+** all we want to do is close the file(s).
+**
+** Returns:
+** none.
+*/
+
+void
+mci_uncache(mcislot, doquit)
+ register MCI **mcislot;
+ bool doquit;
+{
+ register MCI *mci;
+ extern ENVELOPE BlankEnvelope;
+
+ mci = *mcislot;
+ if (mci == NULL)
+ return;
+ *mcislot = NULL;
+ if (mci->mci_host == NULL)
+ return;
+
+ mci_unlock_host(mci);
+
+ if (tTd(42, 5))
+ printf("mci_uncache: uncaching %lx (%s) from slot %d (%d)\n",
+ (u_long) mci, mci->mci_host,
+ (int)(mcislot - MciCache), doquit);
+ if (tTd(91, 100))
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "mci_uncache: uncaching %x (%.100s) from slot %d (%d)",
+ mci, mci->mci_host, mcislot - MciCache, doquit);
+
+#if SMTP
+ if (doquit)
+ {
+ message("Closing connection to %s", mci->mci_host);
+
+ mci->mci_flags &= ~MCIF_CACHED;
+
+ /* only uses the envelope to flush the transcript file */
+ if (mci->mci_state != MCIS_CLOSED)
+ smtpquit(mci->mci_mailer, mci, &BlankEnvelope);
+#ifdef XLA
+ xla_host_end(mci->mci_host);
+#endif
+ }
+ else
+#endif
+ {
+ if (mci->mci_in != NULL)
+ xfclose(mci->mci_in, "mci_uncache", "mci_in");
+ if (mci->mci_out != NULL)
+ xfclose(mci->mci_out, "mci_uncache", "mci_out");
+ mci->mci_in = mci->mci_out = NULL;
+ mci->mci_state = MCIS_CLOSED;
+ mci->mci_exitstat = EX_OK;
+ mci->mci_errno = 0;
+ mci->mci_flags = 0;
+ }
+}
+ /*
+** MCI_FLUSH -- flush the entire cache
+**
+** Parameters:
+** doquit -- if TRUE, send QUIT protocol.
+** if FALSE, just close the connection.
+** allbut -- but leave this one open.
+**
+** Returns:
+** none.
+*/
+
+void
+mci_flush(doquit, allbut)
+ bool doquit;
+ MCI *allbut;
+{
+ register int i;
+
+ if (MciCache == NULL)
+ return;
+
+ for (i = 0; i < MaxMciCache; i++)
+ if (allbut != MciCache[i])
+ mci_uncache(&MciCache[i], doquit);
+}
+ /*
+** MCI_GET -- get information about a particular host
+*/
+
+MCI *
+mci_get(host, m)
+ char *host;
+ MAILER *m;
+{
+ register MCI *mci;
+ register STAB *s;
+
+#if DAEMON
+ extern SOCKADDR CurHostAddr;
+
+ /* clear CurHostAddr so we don't get a bogus address with this name */
+ bzero(&CurHostAddr, sizeof CurHostAddr);
+#endif
+
+ /* clear out any expired connections */
+ (void) mci_scan(NULL);
+
+ if (m->m_mno < 0)
+ syserr("negative mno %d (%s)", m->m_mno, m->m_name);
+ s = stab(host, ST_MCI + m->m_mno, ST_ENTER);
+ mci = &s->s_mci;
+ mci->mci_host = s->s_name;
+
+ if (!mci_load_persistent(mci))
+ {
+ if (tTd(42, 2))
+ printf("mci_get(%s %s): lock failed\n", host, m->m_name);
+ mci->mci_exitstat = EX_TEMPFAIL;
+ mci->mci_state = MCIS_CLOSED;
+ mci->mci_statfile = NULL;
+ return mci;
+ }
+
+ if (tTd(42, 2))
+ {
+ printf("mci_get(%s %s): mci_state=%d, _flags=%x, _exitstat=%d, _errno=%d\n",
+ host, m->m_name, mci->mci_state, mci->mci_flags,
+ mci->mci_exitstat, mci->mci_errno);
+ }
+
+#if SMTP
+ if (mci->mci_state == MCIS_OPEN)
+ {
+ extern int smtpprobe __P((MCI *));
+
+ /* poke the connection to see if it's still alive */
+ (void) smtpprobe(mci);
+
+ /* reset the stored state in the event of a timeout */
+ if (mci->mci_state != MCIS_OPEN)
+ {
+ mci->mci_errno = 0;
+ mci->mci_exitstat = EX_OK;
+ mci->mci_state = MCIS_CLOSED;
+ }
+# if DAEMON
+ else
+ {
+ /* get peer host address for logging reasons only */
+ /* (this should really be in the mci struct) */
+ SOCKADDR_LEN_T socklen = sizeof CurHostAddr;
+
+ (void) getpeername(fileno(mci->mci_in),
+ (struct sockaddr *) &CurHostAddr, &socklen);
+ }
+# endif
+ }
+#endif
+ if (mci->mci_state == MCIS_CLOSED)
+ {
+ time_t now = curtime();
+
+ /* if this info is stale, ignore it */
+ if (now > mci->mci_lastuse + MciInfoTimeout)
+ {
+ mci->mci_lastuse = now;
+ mci->mci_errno = 0;
+ mci->mci_exitstat = EX_OK;
+ }
+ }
+
+ return mci;
+}
+ /*
+** MCI_SETSTAT -- set status codes in MCI structure.
+**
+** Parameters:
+** mci -- the MCI structure to set.
+** xstat -- the exit status code.
+** dstat -- the DSN status code.
+** rstat -- the SMTP status code.
+**
+** Returns:
+** none.
+*/
+
+void
+mci_setstat(mci, xstat, dstat, rstat)
+ MCI *mci;
+ int xstat;
+ char *dstat;
+ char *rstat;
+{
+ /* protocol errors should never be interpreted as sticky */
+ if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL)
+ mci->mci_exitstat = xstat;
+
+ mci->mci_status = dstat;
+ if (mci->mci_rstatus != NULL)
+ free(mci->mci_rstatus);
+ if (rstat != NULL)
+ rstat = newstr(rstat);
+ mci->mci_rstatus = rstat;
+}
+ /*
+** MCI_DUMP -- dump the contents of an MCI structure.
+**
+** Parameters:
+** mci -- the MCI structure to dump.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+*/
+
+struct mcifbits
+{
+ int mcif_bit; /* flag bit */
+ char *mcif_name; /* flag name */
+};
+struct mcifbits MciFlags[] =
+{
+ { MCIF_VALID, "VALID" },
+ { MCIF_TEMP, "TEMP" },
+ { MCIF_CACHED, "CACHED" },
+ { MCIF_ESMTP, "ESMTP" },
+ { MCIF_EXPN, "EXPN" },
+ { MCIF_SIZE, "SIZE" },
+ { MCIF_8BITMIME, "8BITMIME" },
+ { MCIF_7BIT, "7BIT" },
+ { MCIF_MULTSTAT, "MULTSTAT" },
+ { MCIF_INHEADER, "INHEADER" },
+ { MCIF_CVT8TO7, "CVT8TO7" },
+ { MCIF_DSN, "DSN" },
+ { MCIF_8BITOK, "8BITOK" },
+ { MCIF_CVT7TO8, "CVT7TO8" },
+ { MCIF_INMIME, "INMIME" },
+ { 0, NULL }
+};
+
+
+void
+mci_dump(mci, logit)
+ register MCI *mci;
+ bool logit;
+{
+ register char *p;
+ char *sep;
+ char buf[4000];
+ extern char *ctime();
+
+ sep = logit ? " " : "\n\t";
+ p = buf;
+ snprintf(p, SPACELEFT(buf, p), "MCI@%x: ", mci);
+ p += strlen(p);
+ if (mci == NULL)
+ {
+ snprintf(p, SPACELEFT(buf, p), "NULL");
+ goto printit;
+ }
+ snprintf(p, SPACELEFT(buf, p), "flags=%x", mci->mci_flags);
+ p += strlen(p);
+ if (mci->mci_flags != 0)
+ {
+ struct mcifbits *f;
+
+ *p++ = '<';
+ for (f = MciFlags; f->mcif_bit != 0; f++)
+ {
+ if (!bitset(f->mcif_bit, mci->mci_flags))
+ continue;
+ snprintf(p, SPACELEFT(buf, p), "%s,", f->mcif_name);
+ p += strlen(p);
+ }
+ p[-1] = '>';
+ }
+ snprintf(p, SPACELEFT(buf, p),
+ ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
+ sep, mci->mci_errno, mci->mci_herrno,
+ mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep);
+ p += strlen(p);
+ snprintf(p, SPACELEFT(buf, p),
+ "maxsize=%ld, phase=%s, mailer=%s,%s",
+ mci->mci_maxsize,
+ mci->mci_phase == NULL ? "NULL" : mci->mci_phase,
+ mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name,
+ sep);
+ p += strlen(p);
+ snprintf(p, SPACELEFT(buf, p),
+ "status=%s, rstatus=%s,%s",
+ mci->mci_status == NULL ? "NULL" : mci->mci_status,
+ mci->mci_rstatus == NULL ? "NULL" : mci->mci_rstatus,
+ sep);
+ p += strlen(p);
+ snprintf(p, SPACELEFT(buf, p),
+ "host=%s, lastuse=%s",
+ mci->mci_host == NULL ? "NULL" : mci->mci_host,
+ ctime(&mci->mci_lastuse));
+printit:
+ if (logit)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf);
+ else
+ printf("%s\n", buf);
+}
+ /*
+** MCI_DUMP_ALL -- print the entire MCI cache
+**
+** Parameters:
+** logit -- if set, log the result instead of printing
+** to stdout.
+**
+** Returns:
+** none.
+*/
+
+void
+mci_dump_all(logit)
+ bool logit;
+{
+ register int i;
+
+ if (MciCache == NULL)
+ return;
+
+ for (i = 0; i < MaxMciCache; i++)
+ mci_dump(MciCache[i], logit);
+}
+ /*
+** MCI_LOCK_HOST -- Lock host while sending.
+**
+** If we are contacting a host, we'll need to
+** update the status information in the host status
+** file, and if we want to do that, we ought to have
+** locked it. This has the (according to some)
+** desirable effect of serializing connectivity with
+** remote hosts -- i.e.: one connection to a give
+** host at a time.
+**
+** Parameters:
+** mci -- containing the host we want to lock.
+**
+** Returns:
+** EX_OK -- got the lock.
+** EX_TEMPFAIL -- didn't get the lock.
+*/
+
+int
+mci_lock_host(mci)
+ MCI *mci;
+{
+ if (mci == NULL)
+ {
+ if (tTd(56, 1))
+ printf("mci_lock_host: NULL mci\n");
+ return EX_OK;
+ }
+
+ if (!SingleThreadDelivery)
+ return EX_OK;
+
+ return mci_lock_host_statfile(mci);
+}
+
+int
+mci_lock_host_statfile(mci)
+ MCI *mci;
+{
+ int savedErrno = errno;
+ int retVal = EX_OK;
+ char fname[MAXPATHLEN+1];
+
+ if (HostStatDir == NULL || mci->mci_host == NULL)
+ return EX_OK;
+
+ if (tTd(56, 2))
+ printf("mci_lock_host: attempting to lock %s\n",
+ mci->mci_host);
+
+ if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, TRUE) < 0)
+ {
+ /* of course this should never happen */
+ if (tTd(56, 2))
+ printf("mci_lock_host: Failed to generate host path for %s\n",
+ mci->mci_host);
+
+ retVal = EX_TEMPFAIL;
+ goto cleanup;
+ }
+
+ mci->mci_statfile = safefopen(fname, O_RDWR, FileMode,
+ SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT);
+
+ if (mci->mci_statfile == NULL)
+ {
+ syserr("mci_lock_host: cannot create host lock file %s",
+ fname);
+ goto cleanup;
+ }
+
+ if (!lockfile(fileno(mci->mci_statfile), fname, "", LOCK_EX|LOCK_NB))
+ {
+ if (tTd(56, 2))
+ printf("mci_lock_host: couldn't get lock on %s\n",
+ fname);
+ fclose(mci->mci_statfile);
+ mci->mci_statfile = NULL;
+ retVal = EX_TEMPFAIL;
+ goto cleanup;
+ }
+
+ if (tTd(56, 12) && mci->mci_statfile != NULL)
+ printf("mci_lock_host: Sanity check -- lock is good\n");
+
+cleanup:
+ errno = savedErrno;
+ return retVal;
+}
+ /*
+** MCI_UNLOCK_HOST -- unlock host
+**
+** Clean up the lock on a host, close the file, let
+** someone else use it.
+**
+** Parameters:
+** mci -- us.
+**
+** Returns:
+** nothing.
+*/
+
+void
+mci_unlock_host(mci)
+ MCI *mci;
+{
+ int saveErrno = errno;
+
+ if (mci == NULL)
+ {
+ if (tTd(56, 1))
+ printf("mci_unlock_host: NULL mci\n");
+ return;
+ }
+
+ if (HostStatDir == NULL || mci->mci_host == NULL)
+ return;
+
+ if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL)
+ {
+ if (tTd(56, 1))
+ printf("mci_unlock_host: stat file already locked\n");
+ }
+ else
+ {
+ if (tTd(56, 2))
+ printf("mci_unlock_host: store prior to unlock\n");
+
+ mci_store_persistent(mci);
+ }
+
+ if (mci->mci_statfile != NULL)
+ {
+ fclose(mci->mci_statfile);
+ mci->mci_statfile = NULL;
+ }
+
+ errno = saveErrno;
+}
+ /*
+** MCI_LOAD_PERSISTENT -- load persistent host info
+**
+** Load information about host that is kept
+** in common for all running sendmails.
+**
+** Parameters:
+** mci -- the host/connection to load persistent info
+** for.
+**
+** Returns:
+** TRUE -- lock was successful
+** FALSE -- lock failed
+*/
+
+bool
+mci_load_persistent(mci)
+ MCI *mci;
+{
+ int saveErrno = errno;
+ bool locked = TRUE;
+ FILE *fp;
+ char fname[MAXPATHLEN+1];
+
+ if (mci == NULL)
+ {
+ if (tTd(56, 1))
+ printf("mci_load_persistent: NULL mci\n");
+ return TRUE;
+ }
+
+ if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL)
+ return TRUE;
+
+ /* Already have the persistent information in memory */
+ if (SingleThreadDelivery && mci->mci_statfile != NULL)
+ return TRUE;
+
+ if (tTd(56, 1))
+ printf("mci_load_persistent: Attempting to load persistent information for %s\n",
+ mci->mci_host);
+
+ if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, FALSE) < 0)
+ {
+ /* Not much we can do if the file isn't there... */
+ if (tTd(56, 1))
+ printf("mci_load_persistent: Couldn't generate host path\n");
+ goto cleanup;
+ }
+
+ fp = safefopen(fname, O_RDONLY, FileMode,
+ SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
+ if (fp == NULL)
+ {
+ /* I can't think of any reason this should ever happen */
+ if (tTd(56, 1))
+ printf("mci_load_persistent: open(%s): %s\n",
+ fname, errstring(errno));
+ goto cleanup;
+ }
+
+ FileName = fname;
+ locked = lockfile(fileno(fp), fname, "", LOCK_SH|LOCK_NB);
+ (void) mci_read_persistent(fp, mci);
+ FileName = NULL;
+ if (locked)
+ lockfile(fileno(fp), fname, "", LOCK_UN);
+ fclose(fp);
+
+cleanup:
+ errno = saveErrno;
+ return locked;
+}
+ /*
+** MCI_READ_PERSISTENT -- read persistent host status file
+**
+** Parameters:
+** fp -- the file pointer to read.
+** mci -- the pointer to fill in.
+**
+** Returns:
+** -1 -- if the file was corrupt.
+** 0 -- otherwise.
+**
+** Warning:
+** This code makes the assumption that this data
+** will be read in an atomic fashion, and that the data
+** was written in an atomic fashion. Any other functioning
+** may lead to some form of insanity. This should be
+** perfectly safe due to underlying stdio buffering.
+*/
+
+int
+mci_read_persistent(fp, mci)
+ FILE *fp;
+ register MCI *mci;
+{
+ int ver;
+ register char *p;
+ int saveLineNumber = LineNumber;
+ char buf[MAXLINE];
+
+ if (fp == NULL)
+ syserr("mci_read_persistent: NULL fp");
+ if (mci == NULL)
+ syserr("mci_read_persistent: NULL mci");
+ if (tTd(56, 93))
+ {
+ printf("mci_read_persistent: fp=%lx, mci=", (u_long) fp);
+ mci_dump(mci, FALSE);
+ }
+
+ mci->mci_status = NULL;
+ if (mci->mci_rstatus != NULL)
+ free(mci->mci_rstatus);
+ mci->mci_rstatus = NULL;
+
+ rewind(fp);
+ ver = -1;
+ LineNumber = 0;
+ while (fgets(buf, sizeof buf, fp) != NULL)
+ {
+ LineNumber++;
+ p = strchr(buf, '\n');
+ if (p != NULL)
+ *p = '\0';
+ switch (buf[0])
+ {
+ case 'V': /* version stamp */
+ ver = atoi(&buf[1]);
+ if (ver < 0 || ver > 0)
+ syserr("Unknown host status version %d: %d max",
+ ver, 0);
+ break;
+
+ case 'E': /* UNIX error number */
+ mci->mci_errno = atoi(&buf[1]);
+ break;
+
+ case 'H': /* DNS error number */
+ mci->mci_herrno = atoi(&buf[1]);
+ break;
+
+ case 'S': /* UNIX exit status */
+ mci->mci_exitstat = atoi(&buf[1]);
+ break;
+
+ case 'D': /* DSN status */
+ mci->mci_status = newstr(&buf[1]);
+ break;
+
+ case 'R': /* SMTP status */
+ mci->mci_rstatus = newstr(&buf[1]);
+ break;
+
+ case 'U': /* last usage time */
+ mci->mci_lastuse = atol(&buf[1]);
+ break;
+
+ case '.': /* end of file */
+ return 0;
+
+ default:
+ sm_syslog(LOG_CRIT, NOQID,
+ "%s: line %d: Unknown host status line \"%s\"",
+ FileName == NULL ? mci->mci_host : FileName,
+ LineNumber, buf);
+ LineNumber = saveLineNumber;
+ return -1;
+ }
+ }
+ LineNumber = saveLineNumber;
+ if (ver < 0)
+ return -1;
+ return 0;
+}
+ /*
+** MCI_STORE_PERSISTENT -- Store persistent MCI information
+**
+** Store information about host that is kept
+** in common for all running sendmails.
+**
+** Parameters:
+** mci -- the host/connection to store persistent info for.
+**
+** Returns:
+** none.
+*/
+
+void
+mci_store_persistent(mci)
+ MCI *mci;
+{
+ int saveErrno = errno;
+
+ if (mci == NULL)
+ {
+ if (tTd(56, 1))
+ printf("mci_store_persistent: NULL mci\n");
+ return;
+ }
+
+ if (HostStatDir == NULL || mci->mci_host == NULL)
+ return;
+
+ if (tTd(56, 1))
+ printf("mci_store_persistent: Storing information for %s\n",
+ mci->mci_host);
+
+ if (mci->mci_statfile == NULL)
+ {
+ if (tTd(56, 1))
+ printf("mci_store_persistent: no statfile\n");
+ return;
+ }
+
+ rewind(mci->mci_statfile);
+#if !NOFTRUNCATE
+ (void) ftruncate(fileno(mci->mci_statfile), (off_t) 0);
+#endif
+
+ fprintf(mci->mci_statfile, "V0\n");
+ fprintf(mci->mci_statfile, "E%d\n", mci->mci_errno);
+ fprintf(mci->mci_statfile, "H%d\n", mci->mci_herrno);
+ fprintf(mci->mci_statfile, "S%d\n", mci->mci_exitstat);
+ if (mci->mci_status != NULL)
+ fprintf(mci->mci_statfile, "D%.80s\n",
+ denlstring(mci->mci_status, TRUE, FALSE));
+ if (mci->mci_rstatus != NULL)
+ fprintf(mci->mci_statfile, "R%.80s\n",
+ denlstring(mci->mci_rstatus, TRUE, FALSE));
+ fprintf(mci->mci_statfile, "U%ld\n", (long)(mci->mci_lastuse));
+ fprintf(mci->mci_statfile, ".\n");
+
+ fflush(mci->mci_statfile);
+
+ errno = saveErrno;
+ return;
+}
+ /*
+** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree
+**
+** Recursively find all the mci host files in `pathname'. Default to
+** main host status directory if no path is provided.
+** Call (*action)(pathname, host) for each file found.
+**
+** Note: all information is collected in a list before it is processed.
+** This may not be the best way to do it, but it seems safest, since
+** the file system would be touched while we are attempting to traverse
+** the directory tree otherwise (during purges).
+**
+** Parameters:
+** action -- function to call on each node. If returns < 0,
+** return immediately.
+** pathname -- root of tree. If null, use main host status
+** directory.
+**
+** Returns:
+** < 0 -- if any action routine returns a negative value, that
+** value is returned.
+** 0 -- if we successfully went to completion.
+*/
+
+int
+mci_traverse_persistent(action, pathname)
+ int (*action)();
+ char *pathname;
+{
+ struct stat statbuf;
+ DIR *d;
+ int ret;
+
+ if (pathname == NULL)
+ pathname = HostStatDir;
+ if (pathname == NULL)
+ return -1;
+
+ if (tTd(56, 1))
+ printf("mci_traverse: pathname is %s\n", pathname);
+
+ ret = stat(pathname, &statbuf);
+ if (ret < 0)
+ {
+ if (tTd(56, 2))
+ printf("mci_traverse: Failed to stat %s: %s\n",
+ pathname, errstring(errno));
+ return ret;
+ }
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ struct dirent *e;
+ char *newptr;
+ char newpath[MAXPATHLEN+1];
+
+ if ((d = opendir(pathname)) == NULL)
+ {
+ if (tTd(56, 2))
+ printf("mci_traverse: opendir %s: %s\n",
+ pathname, errstring(errno));
+ return -1;
+ }
+
+ if (strlen(pathname) >= sizeof newpath - MAXNAMLEN - 3)
+ {
+ if (tTd(56, 2))
+ printf("mci_traverse: path \"%s\" too long",
+ pathname);
+ return -1;
+ }
+ strcpy(newpath, pathname);
+ newptr = newpath + strlen(newpath);
+ *newptr++ = '/';
+
+ while ((e = readdir(d)) != NULL)
+ {
+ if (e->d_name[0] == '.')
+ continue;
+
+ strncpy(newptr, e->d_name,
+ sizeof newpath - (newptr - newpath) - 1);
+ newpath[sizeof newpath - 1] = '\0';
+
+ ret = mci_traverse_persistent(action, newpath);
+ if (ret < 0)
+ break;
+
+ /*
+ ** The following appears to be
+ ** necessary during purges, since
+ ** we modify the directory structure
+ */
+
+ if (action == mci_purge_persistent)
+ rewinddir(d);
+ }
+
+ /* purge (or whatever) the directory proper */
+ *--newptr = '\0';
+ ret = (*action)(newpath, NULL);
+ closedir(d);
+ }
+ else if (S_ISREG(statbuf.st_mode))
+ {
+ char *end = pathname + strlen(pathname) - 1;
+ char *start;
+ char *scan;
+ char host[MAXHOSTNAMELEN];
+ char *hostptr = host;
+
+ /*
+ ** Reconstruct the host name from the path to the
+ ** persistent information.
+ */
+
+ do
+ {
+ if (hostptr != host)
+ *(hostptr++) = '.';
+ start = end;
+ while (*(start - 1) != '/')
+ start--;
+
+ if (*end == '.')
+ end--;
+
+ for (scan = start; scan <= end; scan++)
+ *(hostptr++) = *scan;
+
+ end = start - 2;
+ } while (*end == '.');
+
+ *hostptr = '\0';
+
+ /*
+ ** Do something with the file containing the persistent
+ ** information.
+ */
+ ret = (*action)(pathname, host);
+ }
+
+ return ret;
+}
+ /*
+** MCI_PRINT_PERSISTENT -- print persisten info
+**
+** Dump the persistent information in the file 'pathname'
+**
+** Parameters:
+** pathname -- the pathname to the status file.
+** hostname -- the corresponding host name.
+**
+** Returns:
+** 0
+*/
+
+int
+mci_print_persistent(pathname, hostname)
+ char *pathname;
+ char *hostname;
+{
+ static int initflag = FALSE;
+ FILE *fp;
+ int width = Verbose ? 78 : 25;
+ bool locked;
+ MCI mcib;
+
+ /* skip directories */
+ if (hostname == NULL)
+ return 0;
+
+ if (!initflag)
+ {
+ initflag = TRUE;
+ printf(" -------------- Hostname --------------- How long ago ---------Results---------\n");
+ }
+
+ fp = safefopen(pathname, O_RDWR, FileMode,
+ SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH);
+
+ if (fp == NULL)
+ {
+ if (tTd(56, 1))
+ printf("mci_print_persistent: cannot open %s: %s\n",
+ pathname, errstring(errno));
+ return 0;
+ }
+
+ FileName = pathname;
+ bzero(&mcib, sizeof mcib);
+ if (mci_read_persistent(fp, &mcib) < 0)
+ {
+ syserr("%s: could not read status file", pathname);
+ fclose(fp);
+ FileName = NULL;
+ return 0;
+ }
+
+ locked = !lockfile(fileno(fp), pathname, "", LOCK_EX|LOCK_NB);
+ fclose(fp);
+ FileName = NULL;
+
+ printf("%c%-39s %12s ",
+ locked ? '*' : ' ', hostname,
+ pintvl(curtime() - mcib.mci_lastuse, TRUE));
+ if (mcib.mci_rstatus != NULL)
+ printf("%.*s\n", width, mcib.mci_rstatus);
+ else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0)
+ printf("Deferred: %.*s\n", width - 10, errstring(mcib.mci_errno));
+ else if (mcib.mci_exitstat != 0)
+ {
+ int i = mcib.mci_exitstat - EX__BASE;
+ extern int N_SysEx;
+ extern char *SysExMsg[];
+
+ if (i < 0 || i >= N_SysEx)
+ {
+ char buf[80];
+
+ snprintf(buf, sizeof buf, "Unknown mailer error %d",
+ mcib.mci_exitstat);
+ printf("%.*s\n", width, buf);
+ }
+ else
+ printf("%.*s\n", width, &(SysExMsg[i])[5]);
+ }
+ else if (mcib.mci_errno == 0)
+ printf("OK\n");
+ else
+ printf("OK: %.*s\n", width - 4, errstring(mcib.mci_errno));
+
+ return 0;
+}
+ /*
+** MCI_PURGE_PERSISTENT -- Remove a persistence status file.
+**
+** Parameters:
+** pathname -- path to the status file.
+** hostname -- name of host corresponding to that file.
+** NULL if this is a directory (domain).
+**
+** Returns:
+** 0
+*/
+
+int
+mci_purge_persistent(pathname, hostname)
+ char *pathname;
+ char *hostname;
+{
+ char *end = pathname + strlen(pathname) - 1;
+
+ if (tTd(56, 1))
+ printf("mci_purge_persistent: purging %s\n", pathname);
+
+ if (hostname != NULL)
+ {
+ /* remove the file */
+ if (unlink(pathname) < 0)
+ {
+ if (tTd(56, 2))
+ printf("mci_purge_persistent: failed to unlink %s: %s\n",
+ pathname, errstring(errno));
+ }
+ }
+ else
+ {
+ /* remove the directory */
+ if (*end != '.')
+ return 0;
+
+ if (tTd(56, 1))
+ printf("mci_purge_persistent: dpurge %s\n", pathname);
+
+ if (rmdir(pathname) < 0)
+ {
+ if (tTd(56, 2))
+ printf("mci_purge_persistent: rmdir %s: %s\n",
+ pathname, errstring(errno));
+ }
+
+ }
+
+ return 0;
+}
+ /*
+** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname
+**
+** Given `host', convert from a.b.c to $QueueDir/.hoststat/c./b./a,
+** putting the result into `path'. if `createflag' is set, intervening
+** directories will be created as needed.
+**
+** Parameters:
+** host -- host name to convert from.
+** path -- place to store result.
+** pathlen -- length of path buffer.
+** createflag -- if set, create intervening directories as
+** needed.
+**
+** Returns:
+** 0 -- success
+** -1 -- failure
+*/
+
+int
+mci_generate_persistent_path(host, path, pathlen, createflag)
+ const char *host;
+ char *path;
+ int pathlen;
+ bool createflag;
+{
+ char *elem, *p, *x, ch;
+ int ret = 0;
+ int len;
+ char t_host[MAXHOSTNAMELEN];
+
+ /*
+ ** Rationality check the arguments.
+ */
+
+ if (host == NULL)
+ {
+ syserr("mci_generate_persistent_path: null host");
+ return -1;
+ }
+ if (path == NULL)
+ {
+ syserr("mci_generate_persistent_path: null path");
+ return -1;
+ }
+
+ if (tTd(56, 80))
+ printf("mci_generate_persistent_path(%s): ", host);
+
+ if (*host == '\0' || *host == '.')
+ return -1;
+
+ /* make certain this is not a bracketed host number */
+ if (strlen(host) > sizeof t_host - 1)
+ return -1;
+ if (host[0] == '[')
+ strcpy(t_host, host + 1);
+ else
+ strcpy(t_host, host);
+
+ /*
+ ** Delete any trailing dots from the hostname.
+ ** Leave 'elem' pointing at the \0.
+ */
+
+ elem = t_host + strlen(t_host);
+ while (elem > t_host &&
+ (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']')))
+ *--elem = '\0';
+
+ /* check for bogus bracketed address */
+ if (host[0] == '[' && inet_addr(t_host) == INADDR_NONE)
+ return -1;
+
+ /* check for what will be the final length of the path */
+ len = strlen(HostStatDir) + 2;
+ for (p = (char *) t_host; *p != '\0'; p++)
+ {
+ if (*p == '.')
+ len++;
+ len++;
+ if (p[0] == '.' && p[1] == '.')
+ return -1;
+ }
+ if (len > pathlen || len < 1)
+ return -1;
+
+ strcpy(path, HostStatDir);
+ p = path + strlen(path);
+
+ while (elem > t_host)
+ {
+ if (!path_is_dir(path, createflag))
+ {
+ ret = -1;
+ break;
+ }
+ elem--;
+ while (elem >= t_host && *elem != '.')
+ elem--;
+ *p++ = '/';
+ x = elem + 1;
+ while ((ch = *x++) != '\0' && ch != '.')
+ {
+ if (isascii(ch) && isupper(ch))
+ ch = tolower(ch);
+ if (ch == '/')
+ ch = ':'; /* / -> : */
+ *p++ = ch;
+ }
+ if (elem >= t_host)
+ *p++ = '.';
+ *p = '\0';
+ }
+
+ if (tTd(56, 80))
+ {
+ if (ret < 0)
+ printf("FAILURE %d\n", ret);
+ else
+ printf("SUCCESS %s\n", path);
+ }
+
+ return (ret);
+}
diff --git a/contrib/sendmail/src/mime.c b/contrib/sendmail/src/mime.c
new file mode 100644
index 0000000..11a141e
--- /dev/null
+++ b/contrib/sendmail/src/mime.c
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1994, 1996-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1994
+ * The Regents of the University of California. 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.
+ *
+ */
+
+# include "sendmail.h"
+# include <string.h>
+
+#ifndef lint
+static char sccsid[] = "@(#)mime.c 8.66 (Berkeley) 5/19/98";
+#endif /* not lint */
+
+/*
+** MIME support.
+**
+** I am indebted to John Beck of Hewlett-Packard, who contributed
+** his code to me for inclusion. As it turns out, I did not use
+** his code since he used a "minimum change" approach that used
+** several temp files, and I wanted a "minimum impact" approach
+** that would avoid copying. However, looking over his code
+** helped me cement my understanding of the problem.
+**
+** I also looked at, but did not directly use, Nathaniel
+** Borenstein's "code.c" module. Again, it functioned as
+** a file-to-file translator, which did not fit within my
+** design bounds, but it was a useful base for understanding
+** the problem.
+*/
+
+#if MIME8TO7
+
+/* character set for hex and base64 encoding */
+char Base16Code[] = "0123456789ABCDEF";
+char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/* types of MIME boundaries */
+#define MBT_SYNTAX 0 /* syntax error */
+#define MBT_NOTSEP 1 /* not a boundary */
+#define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */
+#define MBT_FINAL 3 /* final boundary (trailing -- included) */
+
+static char *MimeBoundaryNames[] =
+{
+ "SYNTAX", "NOTSEP", "INTERMED", "FINAL"
+};
+
+bool MapNLtoCRLF;
+
+extern int mimeboundary __P((char *, char **));
+ /*
+** MIME8TO7 -- output 8 bit body in 7 bit format
+**
+** The header has already been output -- this has to do the
+** 8 to 7 bit conversion. It would be easy if we didn't have
+** to deal with nested formats (multipart/xxx and message/rfc822).
+**
+** We won't be called if we don't have to do a conversion, and
+** appropriate MIME-Version: and Content-Type: fields have been
+** output. Any Content-Transfer-Encoding: field has not been
+** output, and we can add it here.
+**
+** Parameters:
+** mci -- mailer connection information.
+** header -- the header for this body part.
+** e -- envelope.
+** boundaries -- the currently pending message boundaries.
+** NULL if we are processing the outer portion.
+** flags -- to tweak processing.
+**
+** Returns:
+** An indicator of what terminated the message part:
+** MBT_FINAL -- the final boundary
+** MBT_INTERMED -- an intermediate boundary
+** MBT_NOTSEP -- an end of file
+*/
+
+struct args
+{
+ char *field; /* name of field */
+ char *value; /* value of that field */
+};
+
+int
+mime8to7(mci, header, e, boundaries, flags)
+ register MCI *mci;
+ HDR *header;
+ register ENVELOPE *e;
+ char **boundaries;
+ int flags;
+{
+ register char *p;
+ int linelen;
+ int bt;
+ off_t offset;
+ size_t sectionsize, sectionhighbits;
+ int i;
+ char *type;
+ char *subtype;
+ char *cte;
+ char **pvp;
+ int argc = 0;
+ char *bp;
+ bool use_qp = FALSE;
+ struct args argv[MAXMIMEARGS];
+ char bbuf[128];
+ char buf[MAXLINE];
+ char pvpbuf[MAXLINE];
+ extern u_char MimeTokenTab[256];
+ extern int mime_getchar __P((FILE *, char **, int *));
+ extern int mime_getchar_crlf __P((FILE *, char **, int *));
+
+ if (tTd(43, 1))
+ {
+ printf("mime8to7: flags = %x, boundaries =", flags);
+ if (boundaries[0] == NULL)
+ printf(" <none>");
+ else
+ {
+ for (i = 0; boundaries[i] != NULL; i++)
+ printf(" %s", boundaries[i]);
+ }
+ printf("\n");
+ }
+ MapNLtoCRLF = TRUE;
+ p = hvalue("Content-Transfer-Encoding", header);
+ if (p == NULL ||
+ (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
+ MimeTokenTab)) == NULL ||
+ pvp[0] == NULL)
+ {
+ cte = NULL;
+ }
+ else
+ {
+ cataddr(pvp, NULL, buf, sizeof buf, '\0');
+ cte = newstr(buf);
+ }
+
+ type = subtype = NULL;
+ p = hvalue("Content-Type", header);
+ if (p == NULL)
+ {
+ if (bitset(M87F_DIGEST, flags))
+ p = "message/rfc822";
+ else
+ p = "text/plain";
+ }
+ if (p != NULL &&
+ (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
+ MimeTokenTab)) != NULL &&
+ pvp[0] != NULL)
+ {
+ if (tTd(43, 40))
+ {
+ for (i = 0; pvp[i] != NULL; i++)
+ printf("pvp[%d] = \"%s\"\n", i, pvp[i]);
+ }
+ type = *pvp++;
+ if (*pvp != NULL && strcmp(*pvp, "/") == 0 &&
+ *++pvp != NULL)
+ {
+ subtype = *pvp++;
+ }
+
+ /* break out parameters */
+ while (*pvp != NULL && argc < MAXMIMEARGS)
+ {
+ /* skip to semicolon separator */
+ while (*pvp != NULL && strcmp(*pvp, ";") != 0)
+ pvp++;
+ if (*pvp++ == NULL || *pvp == NULL)
+ break;
+
+ /* extract field name */
+ argv[argc].field = *pvp++;
+
+ /* see if there is a value */
+ if (*pvp != NULL && strcmp(*pvp, "=") == 0 &&
+ (*++pvp == NULL || strcmp(*pvp, ";") != 0))
+ {
+ argv[argc].value = *pvp;
+ argc++;
+ }
+ }
+ }
+
+ /* check for disaster cases */
+ if (type == NULL)
+ type = "-none-";
+ if (subtype == NULL)
+ subtype = "-none-";
+
+ /* don't propogate some flags more than one level into the message */
+ flags &= ~M87F_DIGEST;
+
+ /*
+ ** Check for cases that can not be encoded.
+ **
+ ** For example, you can't encode certain kinds of types
+ ** or already-encoded messages. If we find this case,
+ ** just copy it through.
+ */
+
+ snprintf(buf, sizeof buf, "%.100s/%.100s", type, subtype);
+ if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e')))
+ flags |= M87F_NO8BIT;
+
+#ifdef USE_B_CLASS
+ if (wordinclass(buf, 'b') || wordinclass(type, 'b'))
+ MapNLtoCRLF = FALSE;
+#endif
+ if (wordinclass(buf, 'q') || wordinclass(type, 'q'))
+ use_qp = TRUE;
+
+ /*
+ ** Multipart requires special processing.
+ **
+ ** Do a recursive descent into the message.
+ */
+
+ if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags))
+ {
+ int blen;
+
+ if (strcasecmp(subtype, "digest") == 0)
+ flags |= M87F_DIGEST;
+
+ for (i = 0; i < argc; i++)
+ {
+ if (strcasecmp(argv[i].field, "boundary") == 0)
+ break;
+ }
+ if (i >= argc || argv[i].value == NULL)
+ {
+ syserr("mime8to7: Content-Type: \"%s\": %s boundary",
+ i >= argc ? "missing" : "bogus", p);
+ p = "---";
+
+ /* avoid bounce loops */
+ e->e_flags |= EF_DONT_MIME;
+ }
+ else
+ {
+ p = argv[i].value;
+ stripquotes(p);
+ }
+ blen = strlen(p);
+ if (blen > sizeof bbuf - 1)
+ {
+ syserr("mime8to7: multipart boundary \"%s\" too long",
+ p);
+ blen = sizeof bbuf - 1;
+
+ /* avoid bounce loops */
+ e->e_flags |= EF_DONT_MIME;
+ }
+ strncpy(bbuf, p, blen);
+ bbuf[blen] = '\0';
+ if (tTd(43, 1))
+ printf("mime8to7: multipart boundary \"%s\"\n", bbuf);
+ for (i = 0; i < MAXMIMENESTING; i++)
+ if (boundaries[i] == NULL)
+ break;
+ if (i >= MAXMIMENESTING)
+ {
+ syserr("mime8to7: multipart nesting boundary too deep");
+
+ /* avoid bounce loops */
+ e->e_flags |= EF_DONT_MIME;
+ }
+ else
+ {
+ boundaries[i] = bbuf;
+ boundaries[i + 1] = NULL;
+ }
+ mci->mci_flags |= MCIF_INMIME;
+
+ /* skip the early "comment" prologue */
+ putline("", mci);
+ mci->mci_flags &= ~MCIF_INHEADER;
+ while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ {
+ bt = mimeboundary(buf, boundaries);
+ if (bt != MBT_NOTSEP)
+ break;
+ putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
+ if (tTd(43, 99))
+ printf(" ...%s", buf);
+ }
+ if (feof(e->e_dfp))
+ bt = MBT_FINAL;
+ while (bt != MBT_FINAL)
+ {
+ auto HDR *hdr = NULL;
+
+ snprintf(buf, sizeof buf, "--%s", bbuf);
+ putline(buf, mci);
+ if (tTd(43, 35))
+ printf(" ...%s\n", buf);
+ collect(e->e_dfp, FALSE, &hdr, e);
+ if (tTd(43, 101))
+ putline("+++after collect", mci);
+ putheader(mci, hdr, e);
+ if (tTd(43, 101))
+ putline("+++after putheader", mci);
+ bt = mime8to7(mci, hdr, e, boundaries, flags);
+ }
+ snprintf(buf, sizeof buf, "--%s--", bbuf);
+ putline(buf, mci);
+ if (tTd(43, 35))
+ printf(" ...%s\n", buf);
+ boundaries[i] = NULL;
+ mci->mci_flags &= ~MCIF_INMIME;
+
+ /* skip the late "comment" epilogue */
+ while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ {
+ bt = mimeboundary(buf, boundaries);
+ if (bt != MBT_NOTSEP)
+ break;
+ putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT);
+ if (tTd(43, 99))
+ printf(" ...%s", buf);
+ }
+ if (feof(e->e_dfp))
+ bt = MBT_FINAL;
+ if (tTd(43, 3))
+ printf("\t\t\tmime8to7=>%s (multipart)\n",
+ MimeBoundaryNames[bt]);
+ return bt;
+ }
+
+ /*
+ ** Message/xxx types -- recurse exactly once.
+ **
+ ** Class 's' is predefined to have "rfc822" only.
+ */
+
+ if (strcasecmp(type, "message") == 0)
+ {
+ if (!wordinclass(subtype, 's'))
+ {
+ flags |= M87F_NO8BIT;
+ }
+ else
+ {
+ auto HDR *hdr = NULL;
+
+ putline("", mci);
+
+ mci->mci_flags |= MCIF_INMIME;
+ collect(e->e_dfp, FALSE, &hdr, e);
+ if (tTd(43, 101))
+ putline("+++after collect", mci);
+ putheader(mci, hdr, e);
+ if (tTd(43, 101))
+ putline("+++after putheader", mci);
+ if (hvalue("MIME-Version", hdr) == NULL)
+ putline("MIME-Version: 1.0", mci);
+ bt = mime8to7(mci, hdr, e, boundaries, flags);
+ mci->mci_flags &= ~MCIF_INMIME;
+ return bt;
+ }
+ }
+
+ /*
+ ** Non-compound body type
+ **
+ ** Compute the ratio of seven to eight bit characters;
+ ** use that as a heuristic to decide how to do the
+ ** encoding.
+ */
+
+ sectionsize = sectionhighbits = 0;
+ if (!bitset(M87F_NO8BIT, flags))
+ {
+ /* remember where we were */
+ offset = ftell(e->e_dfp);
+ if (offset == -1)
+ syserr("mime8to7: cannot ftell on df%s", e->e_id);
+
+ /* do a scan of this body type to count character types */
+ while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ {
+ if (mimeboundary(buf, boundaries) != MBT_NOTSEP)
+ break;
+ for (p = buf; *p != '\0'; p++)
+ {
+ /* count bytes with the high bit set */
+ sectionsize++;
+ if (bitset(0200, *p))
+ sectionhighbits++;
+ }
+
+ /*
+ ** Heuristic: if 1/4 of the first 4K bytes are 8-bit,
+ ** assume base64. This heuristic avoids double-reading
+ ** large graphics or video files.
+ */
+
+ if (sectionsize >= 4096 &&
+ sectionhighbits > sectionsize / 4)
+ break;
+ }
+
+ /* return to the original offset for processing */
+ /* XXX use relative seeks to handle >31 bit file sizes? */
+ if (fseek(e->e_dfp, offset, SEEK_SET) < 0)
+ syserr("mime8to7: cannot fseek on df%s", e->e_id);
+ else
+ clearerr(e->e_dfp);
+ }
+
+ /*
+ ** Heuristically determine encoding method.
+ ** If more than 1/8 of the total characters have the
+ ** eighth bit set, use base64; else use quoted-printable.
+ ** However, only encode binary encoded data as base64,
+ ** since otherwise the NL=>CRLF mapping will be a problem.
+ */
+
+ if (tTd(43, 8))
+ {
+ printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n",
+ (long) sectionhighbits, (long) sectionsize,
+ cte == NULL ? "[none]" : cte,
+ type == NULL ? "[none]" : type,
+ subtype == NULL ? "[none]" : subtype);
+ }
+ if (cte != NULL && strcasecmp(cte, "binary") == 0)
+ sectionsize = sectionhighbits;
+ linelen = 0;
+ bp = buf;
+ if (sectionhighbits == 0)
+ {
+ /* no encoding necessary */
+ if (cte != NULL)
+ {
+ snprintf(buf, sizeof buf,
+ "Content-Transfer-Encoding: %.200s", cte);
+ putline(buf, mci);
+ if (tTd(43, 36))
+ printf(" ...%s\n", buf);
+ }
+ putline("", mci);
+ mci->mci_flags &= ~MCIF_INHEADER;
+ while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ {
+ bt = mimeboundary(buf, boundaries);
+ if (bt != MBT_NOTSEP)
+ break;
+ putline(buf, mci);
+ }
+ if (feof(e->e_dfp))
+ bt = MBT_FINAL;
+ }
+ else if (!MapNLtoCRLF ||
+ (sectionsize / 8 < sectionhighbits && !use_qp))
+ {
+ /* use base64 encoding */
+ int c1, c2;
+
+ if (tTd(43, 36))
+ printf(" ...Content-Transfer-Encoding: base64\n");
+ putline("Content-Transfer-Encoding: base64", mci);
+ snprintf(buf, sizeof buf,
+ "X-MIME-Autoconverted: from 8bit to base64 by %s id %s",
+ MyHostName, e->e_id);
+ putline(buf, mci);
+ putline("", mci);
+ mci->mci_flags &= ~MCIF_INHEADER;
+ while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF)
+ {
+ if (linelen > 71)
+ {
+ *bp = '\0';
+ putline(buf, mci);
+ linelen = 0;
+ bp = buf;
+ }
+ linelen += 4;
+ *bp++ = Base64Code[(c1 >> 2)];
+ c1 = (c1 & 0x03) << 4;
+ c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
+ if (c2 == EOF)
+ {
+ *bp++ = Base64Code[c1];
+ *bp++ = '=';
+ *bp++ = '=';
+ break;
+ }
+ c1 |= (c2 >> 4) & 0x0f;
+ *bp++ = Base64Code[c1];
+ c1 = (c2 & 0x0f) << 2;
+ c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt);
+ if (c2 == EOF)
+ {
+ *bp++ = Base64Code[c1];
+ *bp++ = '=';
+ break;
+ }
+ c1 |= (c2 >> 6) & 0x03;
+ *bp++ = Base64Code[c1];
+ *bp++ = Base64Code[c2 & 0x3f];
+ }
+ *bp = '\0';
+ putline(buf, mci);
+ }
+ else
+ {
+ /* use quoted-printable encoding */
+ int c1, c2;
+ int fromstate;
+ BITMAP badchars;
+
+ /* set up map of characters that must be mapped */
+ clrbitmap(badchars);
+ for (c1 = 0x00; c1 < 0x20; c1++)
+ setbitn(c1, badchars);
+ clrbitn('\t', badchars);
+ for (c1 = 0x7f; c1 < 0x100; c1++)
+ setbitn(c1, badchars);
+ setbitn('=', badchars);
+ if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags))
+ for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++)
+ setbitn(*p, badchars);
+
+ if (tTd(43, 36))
+ printf(" ...Content-Transfer-Encoding: quoted-printable\n");
+ putline("Content-Transfer-Encoding: quoted-printable", mci);
+ snprintf(buf, sizeof buf,
+ "X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s",
+ MyHostName, e->e_id);
+ putline(buf, mci);
+ putline("", mci);
+ mci->mci_flags &= ~MCIF_INHEADER;
+ fromstate = 0;
+ c2 = '\n';
+ while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF)
+ {
+ if (c1 == '\n')
+ {
+ if (c2 == ' ' || c2 == '\t')
+ {
+ *bp++ = '=';
+ *bp++ = Base16Code[(c2 >> 4) & 0x0f];
+ *bp++ = Base16Code[c2 & 0x0f];
+ }
+ if (buf[0] == '.' && bp == &buf[1])
+ {
+ buf[0] = '=';
+ *bp++ = Base16Code[('.' >> 4) & 0x0f];
+ *bp++ = Base16Code['.' & 0x0f];
+ }
+ *bp = '\0';
+ putline(buf, mci);
+ linelen = fromstate = 0;
+ bp = buf;
+ c2 = c1;
+ continue;
+ }
+ if (c2 == ' ' && linelen == 4 && fromstate == 4 &&
+ bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
+ {
+ *bp++ = '=';
+ *bp++ = '2';
+ *bp++ = '0';
+ linelen += 3;
+ }
+ else if (c2 == ' ' || c2 == '\t')
+ {
+ *bp++ = c2;
+ linelen++;
+ }
+ if (linelen > 72 &&
+ (linelen > 75 || c1 != '.' ||
+ (linelen > 73 && c2 == '.')))
+ {
+ if (linelen > 73 && c2 == '.')
+ bp--;
+ else
+ c2 = '\n';
+ *bp++ = '=';
+ *bp = '\0';
+ putline(buf, mci);
+ linelen = fromstate = 0;
+ bp = buf;
+ if (c2 == '.')
+ {
+ *bp++ = '.';
+ linelen++;
+ }
+ }
+ if (bitnset(c1 & 0xff, badchars))
+ {
+ *bp++ = '=';
+ *bp++ = Base16Code[(c1 >> 4) & 0x0f];
+ *bp++ = Base16Code[c1 & 0x0f];
+ linelen += 3;
+ }
+ else if (c1 != ' ' && c1 != '\t')
+ {
+ if (linelen < 4 && c1 == "From"[linelen])
+ fromstate++;
+ *bp++ = c1;
+ linelen++;
+ }
+ c2 = c1;
+ }
+
+ /* output any saved character */
+ if (c2 == ' ' || c2 == '\t')
+ {
+ *bp++ = '=';
+ *bp++ = Base16Code[(c2 >> 4) & 0x0f];
+ *bp++ = Base16Code[c2 & 0x0f];
+ linelen += 3;
+ }
+
+ if (linelen > 0 || boundaries[0] != NULL)
+ {
+ *bp = '\0';
+ putline(buf, mci);
+ }
+
+ }
+ if (tTd(43, 3))
+ printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]);
+ return bt;
+}
+ /*
+** MIME_GETCHAR -- get a character for MIME processing
+**
+** Treats boundaries as EOF.
+**
+** Parameters:
+** fp -- the input file.
+** boundaries -- the current MIME boundaries.
+** btp -- if the return value is EOF, *btp is set to
+** the type of the boundary.
+**
+** Returns:
+** The next character in the input stream.
+*/
+
+int
+mime_getchar(fp, boundaries, btp)
+ register FILE *fp;
+ char **boundaries;
+ int *btp;
+{
+ int c;
+ static u_char *bp = NULL;
+ static int buflen = 0;
+ static bool atbol = TRUE; /* at beginning of line */
+ static int bt = MBT_SYNTAX; /* boundary type of next EOF */
+ static u_char buf[128]; /* need not be a full line */
+
+ if (buflen > 0)
+ {
+ buflen--;
+ return *bp++;
+ }
+ bp = buf;
+ buflen = 0;
+ c = getc(fp);
+ if (c == '\n')
+ {
+ /* might be part of a MIME boundary */
+ *bp++ = c;
+ atbol = TRUE;
+ c = getc(fp);
+ if (c == '\n')
+ {
+ ungetc(c, fp);
+ return c;
+ }
+ }
+ if (c != EOF)
+ *bp++ = c;
+ else
+ bt = MBT_FINAL;
+ if (atbol && c == '-')
+ {
+ /* check for a message boundary */
+ c = getc(fp);
+ if (c != '-')
+ {
+ if (c != EOF)
+ *bp++ = c;
+ else
+ bt = MBT_FINAL;
+ buflen = bp - buf - 1;
+ bp = buf;
+ return *bp++;
+ }
+
+ /* got "--", now check for rest of separator */
+ *bp++ = '-';
+ while (bp < &buf[sizeof buf - 2] &&
+ (c = getc(fp)) != EOF && c != '\n')
+ {
+ *bp++ = c;
+ }
+ *bp = '\0';
+ bt = mimeboundary((char *) &buf[1], boundaries);
+ switch (bt)
+ {
+ case MBT_FINAL:
+ case MBT_INTERMED:
+ /* we have a message boundary */
+ buflen = 0;
+ *btp = bt;
+ return EOF;
+ }
+
+ atbol = c == '\n';
+ if (c != EOF)
+ *bp++ = c;
+ }
+
+ buflen = bp - buf - 1;
+ if (buflen < 0)
+ {
+ *btp = bt;
+ return EOF;
+ }
+ bp = buf;
+ return *bp++;
+}
+ /*
+** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF
+**
+** Parameters:
+** fp -- the input file.
+** boundaries -- the current MIME boundaries.
+** btp -- if the return value is EOF, *btp is set to
+** the type of the boundary.
+**
+** Returns:
+** The next character in the input stream.
+*/
+
+int
+mime_getchar_crlf(fp, boundaries, btp)
+ register FILE *fp;
+ char **boundaries;
+ int *btp;
+{
+ static bool sendlf = FALSE;
+ int c;
+
+ if (sendlf)
+ {
+ sendlf = FALSE;
+ return '\n';
+ }
+ c = mime_getchar(fp, boundaries, btp);
+ if (c == '\n' && MapNLtoCRLF)
+ {
+ sendlf = TRUE;
+ return '\r';
+ }
+ return c;
+}
+ /*
+** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type
+**
+** Parameters:
+** line -- the input line.
+** boundaries -- the set of currently pending boundaries.
+**
+** Returns:
+** MBT_NOTSEP -- if this is not a separator line
+** MBT_INTERMED -- if this is an intermediate separator
+** MBT_FINAL -- if this is a final boundary
+** MBT_SYNTAX -- if this is a boundary for the wrong
+** enclosure -- i.e., a syntax error.
+*/
+
+int
+mimeboundary(line, boundaries)
+ register char *line;
+ char **boundaries;
+{
+ int type = MBT_NOTSEP;
+ int i;
+ int savec;
+ extern int isboundary __P((char *, char **));
+
+ if (line[0] != '-' || line[1] != '-' || boundaries == NULL)
+ return MBT_NOTSEP;
+ i = strlen(line);
+ if (line[i - 1] == '\n')
+ i--;
+
+ /* strip off trailing whitespace */
+ while (line[i - 1] == ' ' || line[i - 1] == '\t')
+ i--;
+ savec = line[i];
+ line[i] = '\0';
+
+ if (tTd(43, 5))
+ printf("mimeboundary: line=\"%s\"... ", line);
+
+ /* check for this as an intermediate boundary */
+ if (isboundary(&line[2], boundaries) >= 0)
+ type = MBT_INTERMED;
+ else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0)
+ {
+ /* check for a final boundary */
+ line[i - 2] = '\0';
+ if (isboundary(&line[2], boundaries) >= 0)
+ type = MBT_FINAL;
+ line[i - 2] = '-';
+ }
+
+ line[i] = savec;
+ if (tTd(43, 5))
+ printf("%s\n", MimeBoundaryNames[type]);
+ return type;
+}
+ /*
+** DEFCHARSET -- return default character set for message
+**
+** The first choice for character set is for the mailer
+** corresponding to the envelope sender. If neither that
+** nor the global configuration file has a default character
+** set defined, return "unknown-8bit" as recommended by
+** RFC 1428 section 3.
+**
+** Parameters:
+** e -- the envelope for this message.
+**
+** Returns:
+** The default character set for that mailer.
+*/
+
+char *
+defcharset(e)
+ register ENVELOPE *e;
+{
+ if (e != NULL && e->e_from.q_mailer != NULL &&
+ e->e_from.q_mailer->m_defcharset != NULL)
+ return e->e_from.q_mailer->m_defcharset;
+ if (DefaultCharSet != NULL)
+ return DefaultCharSet;
+ return "unknown-8bit";
+}
+ /*
+** ISBOUNDARY -- is a given string a currently valid boundary?
+**
+** Parameters:
+** line -- the current input line.
+** boundaries -- the list of valid boundaries.
+**
+** Returns:
+** The index number in boundaries if the line is found.
+** -1 -- otherwise.
+**
+*/
+
+int
+isboundary(line, boundaries)
+ char *line;
+ char **boundaries;
+{
+ register int i;
+
+ for (i = 0; boundaries[i] != NULL; i++)
+ {
+ if (strcmp(line, boundaries[i]) == 0)
+ return i;
+ }
+ return -1;
+}
+
+#endif /* MIME8TO7 */
+
+#if MIME7TO8
+
+/*
+** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format
+**
+** This is a hack. Supports translating the two 7-bit body-encodings
+** (quoted-printable and base64) to 8-bit coded bodies.
+**
+** There is not much point in supporting multipart here, as the UA
+** will be able to deal with encoded MIME bodies if it can parse MIME
+** multipart messages.
+**
+** Note also that we wont be called unless it is a text/plain MIME
+** message, encoded base64 or QP and mailer flag '9' has been defined
+** on mailer.
+**
+** Contributed by Marius Olaffson <marius@rhi.hi.is>.
+**
+** Parameters:
+** mci -- mailer connection information.
+** header -- the header for this body part.
+** e -- envelope.
+**
+** Returns:
+** none.
+*/
+
+extern int mime_fromqp __P((u_char *, u_char **, int, int));
+
+static char index_64[128] =
+{
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+ 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+
+#define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
+
+
+void
+mime7to8(mci, header, e)
+ register MCI *mci;
+ HDR *header;
+ register ENVELOPE *e;
+{
+ register char *p;
+ char *cte;
+ char **pvp;
+ u_char *fbufp;
+ char buf[MAXLINE];
+ u_char fbuf[MAXLINE + 1];
+ char pvpbuf[MAXLINE];
+ extern u_char MimeTokenTab[256];
+
+ p = hvalue("Content-Transfer-Encoding", header);
+ if (p == NULL ||
+ (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL,
+ MimeTokenTab)) == NULL ||
+ pvp[0] == NULL)
+ {
+ /* "can't happen" -- upper level should have caught this */
+ syserr("mime7to8: unparsable CTE %s", p == NULL ? "<NULL>" : p);
+
+ /* avoid bounce loops */
+ e->e_flags |= EF_DONT_MIME;
+
+ /* cheap failsafe algorithm -- should work on text/plain */
+ if (p != NULL)
+ {
+ snprintf(buf, sizeof buf,
+ "Content-Transfer-Encoding: %s", p);
+ putline(buf, mci);
+ }
+ putline("", mci);
+ mci->mci_flags &= ~MCIF_INHEADER;
+ while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ putline(buf, mci);
+ return;
+ }
+ cataddr(pvp, NULL, buf, sizeof buf, '\0');
+ cte = newstr(buf);
+
+ mci->mci_flags |= MCIF_INHEADER;
+ putline("Content-Transfer-Encoding: 8bit", mci);
+ snprintf(buf, sizeof buf,
+ "X-MIME-Autoconverted: from %.200s to 8bit by %s id %s",
+ cte, MyHostName, e->e_id);
+ putline(buf, mci);
+ putline("", mci);
+ mci->mci_flags &= ~MCIF_INHEADER;
+
+ /*
+ ** Translate body encoding to 8-bit. Supports two types of
+ ** encodings; "base64" and "quoted-printable". Assume qp if
+ ** it is not base64.
+ */
+
+ if (strcasecmp(cte, "base64") == 0)
+ {
+ int c1, c2, c3, c4;
+
+ fbufp = fbuf;
+ while ((c1 = fgetc(e->e_dfp)) != EOF)
+ {
+ if (isascii(c1) && isspace(c1))
+ continue;
+
+ do
+ {
+ c2 = fgetc(e->e_dfp);
+ } while (isascii(c2) && isspace(c2));
+ if (c2 == EOF)
+ break;
+
+ do
+ {
+ c3 = fgetc(e->e_dfp);
+ } while (isascii(c3) && isspace(c3));
+ if (c3 == EOF)
+ break;
+
+ do
+ {
+ c4 = fgetc(e->e_dfp);
+ } while (isascii(c4) && isspace(c4));
+ if (c4 == EOF)
+ break;
+
+ if (c1 == '=' || c2 == '=')
+ continue;
+ c1 = CHAR64(c1);
+ c2 = CHAR64(c2);
+
+ *fbufp = (c1 << 2) | ((c2 & 0x30) >> 4);
+ if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE])
+ {
+ if (*--fbufp != '\n' ||
+ (fbufp > fbuf && *--fbufp != '\r'))
+ fbufp++;
+ putxline((char *) fbuf, fbufp - fbuf,
+ mci, PXLF_MAPFROM);
+ fbufp = fbuf;
+ }
+ if (c3 == '=')
+ continue;
+ c3 = CHAR64(c3);
+ *fbufp = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
+ if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE])
+ {
+ if (*--fbufp != '\n' ||
+ (fbufp > fbuf && *--fbufp != '\r'))
+ fbufp++;
+ putxline((char *) fbuf, fbufp - fbuf,
+ mci, PXLF_MAPFROM);
+ fbufp = fbuf;
+ }
+ if (c4 == '=')
+ continue;
+ c4 = CHAR64(c4);
+ *fbufp = ((c3 & 0x03) << 6) | c4;
+ if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE])
+ {
+ if (*--fbufp != '\n' ||
+ (fbufp > fbuf && *--fbufp != '\r'))
+ fbufp++;
+ putxline((char *) fbuf, fbufp - fbuf,
+ mci, PXLF_MAPFROM);
+ fbufp = fbuf;
+ }
+ }
+ }
+ else
+ {
+ /* quoted-printable */
+ fbufp = fbuf;
+ while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+ {
+ if (mime_fromqp((u_char *) buf, &fbufp, 0,
+ &fbuf[MAXLINE] - fbufp) == 0)
+ continue;
+
+ if (fbufp - fbuf > 0)
+ putxline((char *) fbuf, fbufp - fbuf - 1, mci,
+ PXLF_MAPFROM);
+ fbufp = fbuf;
+ }
+ }
+
+ /* force out partial last line */
+ if (fbufp > fbuf)
+ {
+ *fbufp = '\0';
+ putxline((char *) fbuf, fbufp - fbuf, mci, PXLF_MAPFROM);
+ }
+ if (tTd(43, 3))
+ printf("\t\t\tmime7to8 => %s to 8bit done\n", cte);
+}
+ /*
+** The following is based on Borenstein's "codes.c" module, with simplifying
+** changes as we do not deal with multipart, and to do the translation in-core,
+** with an attempt to prevent overrun of output buffers.
+**
+** What is needed here are changes to defned this code better against
+** bad encodings. Questionable to always return 0xFF for bad mappings.
+*/
+
+static char index_hex[128] =
+{
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
+ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
+};
+
+#define HEXCHAR(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)])
+
+int
+mime_fromqp(infile, outfile, state, maxlen)
+ u_char *infile;
+ u_char **outfile;
+ int state; /* Decoding body (0) or header (1) */
+ int maxlen; /* Max # of chars allowed in outfile */
+{
+ int c1, c2;
+ int nchar = 0;
+
+ while ((c1 = *infile++) != '\0')
+ {
+ if (c1 == '=')
+ {
+ if ((c1 = *infile++) == 0)
+ break;
+
+ if (c1 == '\n' || (c1 = HEXCHAR(c1)) == -1)
+ {
+ /* ignore it */
+ if (state == 0)
+ return 0;
+ }
+ else
+ {
+ do
+ {
+ if ((c2 = *infile++) == '\0')
+ {
+ c2 = -1;
+ break;
+ }
+ } while ((c2 = HEXCHAR(c2)) == -1);
+
+ if (c2 == -1 || ++nchar > maxlen)
+ break;
+
+ *(*outfile)++ = c1 << 4 | c2;
+ }
+ }
+ else
+ {
+ if (state == 1 && c1 == '_')
+ c1 = ' ';
+
+ if (++nchar > maxlen)
+ break;
+
+ *(*outfile)++ = c1;
+
+ if (c1 == '\n')
+ break;
+ }
+ }
+ *(*outfile)++ = '\0';
+ return 1;
+}
+
+
+#endif /* MIME7TO8 */
diff --git a/contrib/sendmail/src/newaliases.1 b/contrib/sendmail/src/newaliases.1
new file mode 100644
index 0000000..acf3245
--- /dev/null
+++ b/contrib/sendmail/src/newaliases.1
@@ -0,0 +1,47 @@
+.\" Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+.\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved.
+.\" Copyright (c) 1985, 1990, 1993
+.\" The Regents of the University of California. 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.
+.\"
+.\"
+.\" @(#)newaliases.1 8.10 (Berkeley) 5/19/98
+.\"
+.Dd May 19, 1998
+.Dt NEWALIASES 1
+.Os BSD 4
+.Sh NAME
+.Nm newaliases
+.Nd rebuild the data base for the mail aliases file
+.Sh SYNOPSIS
+.Nm newaliases
+.Sh DESCRIPTION
+.Nm Newaliases
+rebuilds the random access data base for the mail aliases file
+.Pa /etc/aliases .
+It must be run each time this file is changed in order
+for the change to take effect.
+.Pp
+.Nm Newaliases
+is identical to
+.Dq Li "sendmail -bi" .
+.Pp
+The
+.Nm newaliases
+utility exits 0 on success, and >0 if an error occurs.
+.Sh FILES
+.Bl -tag -width /etc/aliases -compact
+.It Pa /etc/aliases
+The mail aliases file
+.El
+.Sh SEE ALSO
+.Xr aliases 5 ,
+.Xr sendmail 8
+.Sh HISTORY
+The
+.Nm newaliases
+command appeared in
+.Bx 4.0 .
diff --git a/contrib/sendmail/src/parseaddr.c b/contrib/sendmail/src/parseaddr.c
new file mode 100644
index 0000000..c3c89b4
--- /dev/null
+++ b/contrib/sendmail/src/parseaddr.c
@@ -0,0 +1,2547 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parseaddr.c 8.153 (Berkeley) 6/24/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+** PARSEADDR -- Parse an address
+**
+** Parses an address and breaks it up into three parts: a
+** net to transmit the message on, the host to transmit it
+** to, and a user on that host. These are loaded into an
+** ADDRESS header with the values squirreled away if necessary.
+** The "user" part may not be a real user; the process may
+** just reoccur on that machine. For example, on a machine
+** with an arpanet connection, the address
+** csvax.bill@berkeley
+** will break up to a "user" of 'csvax.bill' and a host
+** of 'berkeley' -- to be transmitted over the arpanet.
+**
+** Parameters:
+** addr -- the address to parse.
+** a -- a pointer to the address descriptor buffer.
+** If NULL, a header will be created.
+** flags -- describe detail for parsing. See RF_ definitions
+** in sendmail.h.
+** delim -- the character to terminate the address, passed
+** to prescan.
+** delimptr -- if non-NULL, set to the location of the
+** delim character that was found.
+** e -- the envelope that will contain this address.
+**
+** Returns:
+** A pointer to the address descriptor header (`a' if
+** `a' is non-NULL).
+** NULL on error.
+**
+** Side Effects:
+** none
+*/
+
+/* following delimiters are inherent to the internal algorithms */
+# define DELIMCHARS "()<>,;\r\n" /* default word delimiters */
+
+ADDRESS *
+parseaddr(addr, a, flags, delim, delimptr, e)
+ char *addr;
+ register ADDRESS *a;
+ int flags;
+ int delim;
+ char **delimptr;
+ register ENVELOPE *e;
+{
+ register char **pvp;
+ auto char *delimptrbuf;
+ bool queueup;
+ char pvpbuf[PSBUFSIZE];
+ extern ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
+ extern bool invalidaddr __P((char *, char *));
+ extern void allocaddr __P((ADDRESS *, int, char *));
+
+ /*
+ ** Initialize and prescan address.
+ */
+
+ e->e_to = addr;
+ if (tTd(20, 1))
+ printf("\n--parseaddr(%s)\n", addr);
+
+ if (delimptr == NULL)
+ delimptr = &delimptrbuf;
+
+ pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
+ if (pvp == NULL)
+ {
+ if (tTd(20, 1))
+ printf("parseaddr-->NULL\n");
+ return (NULL);
+ }
+
+ if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr))
+ {
+ if (tTd(20, 1))
+ printf("parseaddr-->bad address\n");
+ return NULL;
+ }
+
+ /*
+ ** Save addr if we are going to have to.
+ **
+ ** We have to do this early because there is a chance that
+ ** the map lookups in the rewriting rules could clobber
+ ** static memory somewhere.
+ */
+
+ if (bitset(RF_COPYPADDR, flags) && addr != NULL)
+ {
+ char savec = **delimptr;
+
+ if (savec != '\0')
+ **delimptr = '\0';
+ e->e_to = addr = newstr(addr);
+ if (savec != '\0')
+ **delimptr = savec;
+ }
+
+ /*
+ ** Apply rewriting rules.
+ ** Ruleset 0 does basic parsing. It must resolve.
+ */
+
+ queueup = FALSE;
+ if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
+ queueup = TRUE;
+ if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL)
+ queueup = TRUE;
+
+
+ /*
+ ** Build canonical address from pvp.
+ */
+
+ a = buildaddr(pvp, a, flags, e);
+
+ /*
+ ** Make local copies of the host & user and then
+ ** transport them out.
+ */
+
+ allocaddr(a, flags, addr);
+ if (bitset(QBADADDR, a->q_flags))
+ return a;
+
+ /*
+ ** If there was a parsing failure, mark it for queueing.
+ */
+
+ if (queueup && OpMode != MD_INITALIAS)
+ {
+ char *msg = "Transient parse error -- message queued for future delivery";
+
+ if (e->e_sendmode == SM_DEFER)
+ msg = "Deferring message until queue run";
+ if (tTd(20, 1))
+ printf("parseaddr: queuing message\n");
+ message(msg);
+ if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
+ e->e_message = newstr(msg);
+ a->q_flags |= QQUEUEUP;
+ a->q_status = "4.4.3";
+ }
+
+ /*
+ ** Compute return value.
+ */
+
+ if (tTd(20, 1))
+ {
+ printf("parseaddr-->");
+ printaddr(a, FALSE);
+ }
+
+ return (a);
+}
+ /*
+** INVALIDADDR -- check for address containing meta-characters
+**
+** Parameters:
+** addr -- the address to check.
+**
+** Returns:
+** TRUE -- if the address has any "wierd" characters
+** FALSE -- otherwise.
+*/
+
+bool
+invalidaddr(addr, delimptr)
+ register char *addr;
+ char *delimptr;
+{
+ char savedelim = '\0';
+
+ if (delimptr != NULL)
+ {
+ savedelim = *delimptr;
+ if (savedelim != '\0')
+ *delimptr = '\0';
+ }
+ if (strlen(addr) > TOBUFSIZE - 2)
+ {
+ usrerr("553 Address too long (%d bytes max)", TOBUFSIZE - 2);
+ goto failure;
+ }
+ for (; *addr != '\0'; addr++)
+ {
+ if ((*addr & 0340) == 0200)
+ break;
+ }
+ if (*addr == '\0')
+ {
+ if (delimptr != NULL && savedelim != '\0')
+ *delimptr = savedelim;
+ return FALSE;
+ }
+ setstat(EX_USAGE);
+ usrerr("553 Address contained invalid control characters");
+failure:
+ if (delimptr != NULL && savedelim != '\0')
+ *delimptr = savedelim;
+ return TRUE;
+}
+ /*
+** ALLOCADDR -- do local allocations of address on demand.
+**
+** Also lowercases the host name if requested.
+**
+** Parameters:
+** a -- the address to reallocate.
+** flags -- the copy flag (see RF_ definitions in sendmail.h
+** for a description).
+** paddr -- the printname of the address.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Copies portions of a into local buffers as requested.
+*/
+
+void
+allocaddr(a, flags, paddr)
+ register ADDRESS *a;
+ int flags;
+ char *paddr;
+{
+ if (tTd(24, 4))
+ printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
+
+ a->q_paddr = paddr;
+
+ if (a->q_user == NULL)
+ a->q_user = "";
+ if (a->q_host == NULL)
+ a->q_host = "";
+
+ if (bitset(RF_COPYPARSE, flags))
+ {
+ a->q_host = newstr(a->q_host);
+ if (a->q_user != a->q_paddr)
+ a->q_user = newstr(a->q_user);
+ }
+
+ if (a->q_paddr == NULL)
+ a->q_paddr = a->q_user;
+}
+ /*
+** PRESCAN -- Prescan name and make it canonical
+**
+** Scans a name and turns it into a set of tokens. This process
+** deletes blanks and comments (in parentheses).
+**
+** This routine knows about quoted strings and angle brackets.
+**
+** There are certain subtleties to this routine. The one that
+** comes to mind now is that backslashes on the ends of names
+** are silently stripped off; this is intentional. The problem
+** is that some versions of sndmsg (like at LBL) set the kill
+** character to something other than @ when reading addresses;
+** so people type "csvax.eric\@berkeley" -- which screws up the
+** berknet mailer.
+**
+** Parameters:
+** addr -- the name to chomp.
+** delim -- the delimiter for the address, normally
+** '\0' or ','; \0 is accepted in any case.
+** If '\t' then we are reading the .cf file.
+** pvpbuf -- place to put the saved text -- note that
+** the pointers are static.
+** pvpbsize -- size of pvpbuf.
+** delimptr -- if non-NULL, set to the location of the
+** terminating delimiter.
+** toktab -- if set, a token table to use for parsing.
+** If NULL, use the default table.
+**
+** Returns:
+** A pointer to a vector of tokens.
+** NULL on error.
+*/
+
+/* states and character types */
+# define OPR 0 /* operator */
+# define ATM 1 /* atom */
+# define QST 2 /* in quoted string */
+# define SPC 3 /* chewing up spaces */
+# define ONE 4 /* pick up one character */
+# define ILL 5 /* illegal character */
+
+# define NSTATES 6 /* number of states */
+# define TYPE 017 /* mask to select state type */
+
+/* meta bits for table */
+# define M 020 /* meta character; don't pass through */
+# define B 040 /* cause a break */
+# define MB M|B /* meta-break */
+
+static short StateTab[NSTATES][NSTATES] =
+{
+ /* oldst chtype> OPR ATM QST SPC ONE ILL */
+ /*OPR*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB },
+ /*ATM*/ { OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB },
+ /*QST*/ { QST, QST, OPR, QST, QST, QST },
+ /*SPC*/ { OPR, ATM, QST, SPC|M, ONE, ILL|MB },
+ /*ONE*/ { OPR, OPR, OPR, OPR, OPR, ILL|MB },
+ /*ILL*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M },
+};
+
+/* token type table -- it gets modified with $o characters */
+static u_char TokTypeTab[256] =
+{
+ /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
+ /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* sp ! " # $ % & ' ( ) * + , - . / */
+ SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* @ A B C D E F G H I J K L M N O */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* P Q R S T U V W X Y Z [ \ ] ^ _ */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* ` a b c d e f g h i j k l m n o */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* p q r s t u v w x y z { | } ~ del */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+
+ /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
+ OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
+ /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
+ OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
+ /* sp ! " # $ % & ' ( ) * + , - . / */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* @ A B C D E F G H I J K L M N O */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* P Q R S T U V W X Y Z [ \ ] ^ _ */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* ` a b c d e f g h i j k l m n o */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* p q r s t u v w x y z { | } ~ del */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+};
+
+/* token type table for MIME parsing */
+u_char MimeTokenTab[256] =
+{
+ /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
+ /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+ /* sp ! " # $ % & ' ( ) * + , - . / */
+ SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
+ /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
+ /* @ A B C D E F G H I J K L M N O */
+ OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* P Q R S T U V W X Y Z [ \ ] ^ _ */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
+ /* ` a b c d e f g h i j k l m n o */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+ /* p q r s t u v w x y z { | } ~ del */
+ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+
+ /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+ /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+ /* sp ! " # $ % & ' ( ) * + , - . / */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+ /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+ /* @ A B C D E F G H I J K L M N O */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+ /* P Q R S T U V W X Y Z [ \ ] ^ _ */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+ /* ` a b c d e f g h i j k l m n o */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+ /* p q r s t u v w x y z { | } ~ del */
+ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+};
+
+
+# define NOCHAR -1 /* signal nothing in lookahead token */
+
+char **
+prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
+ char *addr;
+ int delim;
+ char pvpbuf[];
+ int pvpbsize;
+ char **delimptr;
+ u_char *toktab;
+{
+ register char *p;
+ register char *q;
+ register int c;
+ char **avp;
+ bool bslashmode;
+ bool route_syntax;
+ int cmntcnt;
+ int anglecnt;
+ char *tok;
+ int state;
+ int newstate;
+ char *saveto = CurEnv->e_to;
+ static char *av[MAXATOM+1];
+ static char firsttime = TRUE;
+ extern int errno;
+
+ if (firsttime)
+ {
+ /* initialize the token type table */
+ char obuf[50];
+
+ firsttime = FALSE;
+ if (OperatorChars == NULL)
+ {
+ if (ConfigLevel < 7)
+ OperatorChars = macvalue('o', CurEnv);
+ if (OperatorChars == NULL)
+ OperatorChars = ".:@[]";
+ }
+ expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv);
+ strcat(obuf, DELIMCHARS);
+ for (p = obuf; *p != '\0'; p++)
+ {
+ if (TokTypeTab[*p & 0xff] == ATM)
+ TokTypeTab[*p & 0xff] = OPR;
+ }
+ }
+ if (toktab == NULL)
+ toktab = TokTypeTab;
+
+ /* make sure error messages don't have garbage on them */
+ errno = 0;
+
+ q = pvpbuf;
+ bslashmode = FALSE;
+ route_syntax = FALSE;
+ cmntcnt = 0;
+ anglecnt = 0;
+ avp = av;
+ state = ATM;
+ c = NOCHAR;
+ p = addr;
+ CurEnv->e_to = p;
+ if (tTd(22, 11))
+ {
+ printf("prescan: ");
+ xputs(p);
+ (void) putchar('\n');
+ }
+
+ do
+ {
+ /* read a token */
+ tok = q;
+ for (;;)
+ {
+ /* store away any old lookahead character */
+ if (c != NOCHAR && !bslashmode)
+ {
+ /* see if there is room */
+ if (q >= &pvpbuf[pvpbsize - 5])
+ {
+ usrerr("553 Address too long");
+ if (strlen(addr) > (SIZE_T) MAXNAME)
+ addr[MAXNAME] = '\0';
+ returnnull:
+ if (delimptr != NULL)
+ *delimptr = p;
+ CurEnv->e_to = saveto;
+ return (NULL);
+ }
+
+ /* squirrel it away */
+ *q++ = c;
+ }
+
+ /* read a new input character */
+ c = *p++;
+ if (c == '\0')
+ {
+ /* diagnose and patch up bad syntax */
+ if (state == QST)
+ {
+ usrerr("653 Unbalanced '\"'");
+ c = '"';
+ }
+ else if (cmntcnt > 0)
+ {
+ usrerr("653 Unbalanced '('");
+ c = ')';
+ }
+ else if (anglecnt > 0)
+ {
+ c = '>';
+ usrerr("653 Unbalanced '<'");
+ }
+ else
+ break;
+
+ p--;
+ }
+ else if (c == delim && cmntcnt <= 0 && state != QST)
+ {
+ if (anglecnt <= 0)
+ break;
+
+ /* special case for better error management */
+ if (delim == ',' && !route_syntax)
+ {
+ usrerr("653 Unbalanced '<'");
+ c = '>';
+ p--;
+ }
+ }
+
+ if (tTd(22, 101))
+ printf("c=%c, s=%d; ", c, state);
+
+ /* chew up special characters */
+ *q = '\0';
+ if (bslashmode)
+ {
+ bslashmode = FALSE;
+
+ /* kludge \! for naive users */
+ if (cmntcnt > 0)
+ {
+ c = NOCHAR;
+ continue;
+ }
+ else if (c != '!' || state == QST)
+ {
+ *q++ = '\\';
+ continue;
+ }
+ }
+
+ if (c == '\\')
+ {
+ bslashmode = TRUE;
+ }
+ else if (state == QST)
+ {
+ /* do nothing, just avoid next clauses */
+ }
+ else if (c == '(')
+ {
+ cmntcnt++;
+ c = NOCHAR;
+ }
+ else if (c == ')')
+ {
+ if (cmntcnt <= 0)
+ {
+ usrerr("653 Unbalanced ')'");
+ c = NOCHAR;
+ }
+ else
+ cmntcnt--;
+ }
+ else if (cmntcnt > 0)
+ c = NOCHAR;
+ else if (c == '<')
+ {
+ char *q = p;
+
+ anglecnt++;
+ while (isascii(*q) && isspace(*q))
+ q++;
+ if (*q == '@')
+ route_syntax = TRUE;
+ }
+ else if (c == '>')
+ {
+ if (anglecnt <= 0)
+ {
+ usrerr("653 Unbalanced '>'");
+ c = NOCHAR;
+ }
+ else
+ anglecnt--;
+ route_syntax = FALSE;
+ }
+ else if (delim == ' ' && isascii(c) && isspace(c))
+ c = ' ';
+
+ if (c == NOCHAR)
+ continue;
+
+ /* see if this is end of input */
+ if (c == delim && anglecnt <= 0 && state != QST)
+ break;
+
+ newstate = StateTab[state][toktab[c & 0xff]];
+ if (tTd(22, 101))
+ printf("ns=%02o\n", newstate);
+ state = newstate & TYPE;
+ if (state == ILL)
+ {
+ if (isascii(c) && isprint(c))
+ usrerr("653 Illegal character %c", c);
+ else
+ usrerr("653 Illegal character 0x%02x", c);
+ }
+ if (bitset(M, newstate))
+ c = NOCHAR;
+ if (bitset(B, newstate))
+ break;
+ }
+
+ /* new token */
+ if (tok != q)
+ {
+ *q++ = '\0';
+ if (tTd(22, 36))
+ {
+ printf("tok=");
+ xputs(tok);
+ (void) putchar('\n');
+ }
+ if (avp >= &av[MAXATOM])
+ {
+ usrerr("553 prescan: too many tokens");
+ goto returnnull;
+ }
+ if (q - tok > MAXNAME)
+ {
+ usrerr("553 prescan: token too long");
+ goto returnnull;
+ }
+ *avp++ = tok;
+ }
+ } while (c != '\0' && (c != delim || anglecnt > 0));
+ *avp = NULL;
+ p--;
+ if (delimptr != NULL)
+ *delimptr = p;
+ if (tTd(22, 12))
+ {
+ printf("prescan==>");
+ printav(av);
+ }
+ CurEnv->e_to = saveto;
+ if (av[0] == NULL)
+ {
+ if (tTd(22, 1))
+ printf("prescan: null leading token\n");
+ return (NULL);
+ }
+ return (av);
+}
+ /*
+** REWRITE -- apply rewrite rules to token vector.
+**
+** This routine is an ordered production system. Each rewrite
+** rule has a LHS (called the pattern) and a RHS (called the
+** rewrite); 'rwr' points the the current rewrite rule.
+**
+** For each rewrite rule, 'avp' points the address vector we
+** are trying to match against, and 'pvp' points to the pattern.
+** If pvp points to a special match value (MATCHZANY, MATCHANY,
+** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
+** matched is saved away in the match vector (pointed to by 'mvp').
+**
+** When a match between avp & pvp does not match, we try to
+** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
+** we must also back out the match in mvp. If we reach a
+** MATCHANY or MATCHZANY we just extend the match and start
+** over again.
+**
+** When we finally match, we rewrite the address vector
+** and try over again.
+**
+** Parameters:
+** pvp -- pointer to token vector.
+** ruleset -- the ruleset to use for rewriting.
+** reclevel -- recursion level (to catch loops).
+** e -- the current envelope.
+**
+** Returns:
+** A status code. If EX_TEMPFAIL, higher level code should
+** attempt recovery.
+**
+** Side Effects:
+** pvp is modified.
+*/
+
+struct match
+{
+ char **first; /* first token matched */
+ char **last; /* last token matched */
+ char **pattern; /* pointer to pattern */
+};
+
+# define MAXMATCH 9 /* max params per rewrite */
+
+
+int
+rewrite(pvp, ruleset, reclevel, e)
+ char **pvp;
+ int ruleset;
+ int reclevel;
+ register ENVELOPE *e;
+{
+ register char *ap; /* address pointer */
+ register char *rp; /* rewrite pointer */
+ register char **avp; /* address vector pointer */
+ register char **rvp; /* rewrite vector pointer */
+ register struct match *mlp; /* cur ptr into mlist */
+ register struct rewrite *rwr; /* pointer to current rewrite rule */
+ int ruleno; /* current rule number */
+ int rstat = EX_OK; /* return status */
+ int loopcount;
+ struct match mlist[MAXMATCH]; /* stores match on LHS */
+ char *npvp[MAXATOM+1]; /* temporary space for rebuild */
+ char buf[MAXLINE];
+ extern int callsubr __P((char**, int, ENVELOPE *));
+ extern int sm_strcasecmp __P((char *, char *));
+
+ if (OpMode == MD_TEST || tTd(21, 1))
+ {
+ printf("rewrite: ruleset %3d input:", ruleset);
+ printav(pvp);
+ }
+ if (ruleset < 0 || ruleset >= MAXRWSETS)
+ {
+ syserr("554 rewrite: illegal ruleset number %d", ruleset);
+ return EX_CONFIG;
+ }
+ if (reclevel++ > MaxRuleRecursion)
+ {
+ syserr("rewrite: excessive recursion (max %d), ruleset %d",
+ MaxRuleRecursion, ruleset);
+ return EX_CONFIG;
+ }
+ if (pvp == NULL)
+ return EX_USAGE;
+
+ /*
+ ** Run through the list of rewrite rules, applying
+ ** any that match.
+ */
+
+ ruleno = 1;
+ loopcount = 0;
+ for (rwr = RewriteRules[ruleset]; rwr != NULL; )
+ {
+ int stat;
+
+ /* if already canonical, quit now */
+ if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
+ break;
+
+ if (tTd(21, 12))
+ {
+ printf("-----trying rule:");
+ printav(rwr->r_lhs);
+ }
+
+ /* try to match on this rule */
+ mlp = mlist;
+ rvp = rwr->r_lhs;
+ avp = pvp;
+ if (++loopcount > 100)
+ {
+ syserr("554 Infinite loop in ruleset %d, rule %d",
+ ruleset, ruleno);
+ if (tTd(21, 1))
+ {
+ printf("workspace: ");
+ printav(pvp);
+ }
+ break;
+ }
+
+ while ((ap = *avp) != NULL || *rvp != NULL)
+ {
+ rp = *rvp;
+ if (tTd(21, 35))
+ {
+ printf("ADVANCE rp=");
+ xputs(rp);
+ printf(", ap=");
+ xputs(ap);
+ printf("\n");
+ }
+ if (rp == NULL)
+ {
+ /* end-of-pattern before end-of-address */
+ goto backup;
+ }
+ if (ap == NULL && (*rp & 0377) != MATCHZANY &&
+ (*rp & 0377) != MATCHZERO)
+ {
+ /* end-of-input with patterns left */
+ goto backup;
+ }
+
+ switch (*rp & 0377)
+ {
+ case MATCHCLASS:
+ /* match any phrase in a class */
+ mlp->pattern = rvp;
+ mlp->first = avp;
+ extendclass:
+ ap = *avp;
+ if (ap == NULL)
+ goto backup;
+ mlp->last = avp++;
+ cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0');
+ if (!wordinclass(buf, rp[1]))
+ {
+ if (tTd(21, 36))
+ {
+ printf("EXTEND rp=");
+ xputs(rp);
+ printf(", ap=");
+ xputs(ap);
+ printf("\n");
+ }
+ goto extendclass;
+ }
+ if (tTd(21, 36))
+ printf("CLMATCH\n");
+ mlp++;
+ break;
+
+ case MATCHNCLASS:
+ /* match any token not in a class */
+ if (wordinclass(ap, rp[1]))
+ goto backup;
+
+ /* fall through */
+
+ case MATCHONE:
+ case MATCHANY:
+ /* match exactly one token */
+ mlp->pattern = rvp;
+ mlp->first = avp;
+ mlp->last = avp++;
+ mlp++;
+ break;
+
+ case MATCHZANY:
+ /* match zero or more tokens */
+ mlp->pattern = rvp;
+ mlp->first = avp;
+ mlp->last = avp - 1;
+ mlp++;
+ break;
+
+ case MATCHZERO:
+ /* match zero tokens */
+ break;
+
+ case MACRODEXPAND:
+ /*
+ ** Match against run-time macro.
+ ** This algorithm is broken for the
+ ** general case (no recursive macros,
+ ** improper tokenization) but should
+ ** work for the usual cases.
+ */
+
+ ap = macvalue(rp[1], e);
+ mlp->first = avp;
+ if (tTd(21, 2))
+ printf("rewrite: LHS $&%s => \"%s\"\n",
+ macname(rp[1]),
+ ap == NULL ? "(NULL)" : ap);
+
+ if (ap == NULL)
+ break;
+ while (*ap != '\0')
+ {
+ if (*avp == NULL ||
+ strncasecmp(ap, *avp, strlen(*avp)) != 0)
+ {
+ /* no match */
+ avp = mlp->first;
+ goto backup;
+ }
+ ap += strlen(*avp++);
+ }
+
+ /* match */
+ break;
+
+ default:
+ /* must have exact match */
+ if (sm_strcasecmp(rp, ap))
+ goto backup;
+ avp++;
+ break;
+ }
+
+ /* successful match on this token */
+ rvp++;
+ continue;
+
+ backup:
+ /* match failed -- back up */
+ while (--mlp >= mlist)
+ {
+ rvp = mlp->pattern;
+ rp = *rvp;
+ avp = mlp->last + 1;
+ ap = *avp;
+
+ if (tTd(21, 36))
+ {
+ printf("BACKUP rp=");
+ xputs(rp);
+ printf(", ap=");
+ xputs(ap);
+ printf("\n");
+ }
+
+ if (ap == NULL)
+ {
+ /* run off the end -- back up again */
+ continue;
+ }
+ if ((*rp & 0377) == MATCHANY ||
+ (*rp & 0377) == MATCHZANY)
+ {
+ /* extend binding and continue */
+ mlp->last = avp++;
+ rvp++;
+ mlp++;
+ break;
+ }
+ if ((*rp & 0377) == MATCHCLASS)
+ {
+ /* extend binding and try again */
+ mlp->last = avp;
+ goto extendclass;
+ }
+ }
+
+ if (mlp < mlist)
+ {
+ /* total failure to match */
+ break;
+ }
+ }
+
+ /*
+ ** See if we successfully matched
+ */
+
+ if (mlp < mlist || *rvp != NULL)
+ {
+ if (tTd(21, 10))
+ printf("----- rule fails\n");
+ rwr = rwr->r_next;
+ ruleno++;
+ loopcount = 0;
+ continue;
+ }
+
+ rvp = rwr->r_rhs;
+ if (tTd(21, 12))
+ {
+ printf("-----rule matches:");
+ printav(rvp);
+ }
+
+ rp = *rvp;
+ if ((*rp & 0377) == CANONUSER)
+ {
+ rvp++;
+ rwr = rwr->r_next;
+ ruleno++;
+ loopcount = 0;
+ }
+ else if ((*rp & 0377) == CANONHOST)
+ {
+ rvp++;
+ rwr = NULL;
+ }
+
+ /* substitute */
+ for (avp = npvp; *rvp != NULL; rvp++)
+ {
+ register struct match *m;
+ register char **pp;
+
+ rp = *rvp;
+ if ((*rp & 0377) == MATCHREPL)
+ {
+ /* substitute from LHS */
+ m = &mlist[rp[1] - '1'];
+ if (m < mlist || m >= mlp)
+ {
+ syserr("554 rewrite: ruleset %d: replacement $%c out of bounds",
+ ruleset, rp[1]);
+ return EX_CONFIG;
+ }
+ if (tTd(21, 15))
+ {
+ printf("$%c:", rp[1]);
+ pp = m->first;
+ while (pp <= m->last)
+ {
+ printf(" %lx=\"", (u_long) *pp);
+ (void) fflush(stdout);
+ printf("%s\"", *pp++);
+ }
+ printf("\n");
+ }
+ pp = m->first;
+ while (pp <= m->last)
+ {
+ if (avp >= &npvp[MAXATOM])
+ {
+ syserr("554 rewrite: expansion too long");
+ return EX_DATAERR;
+ }
+ *avp++ = *pp++;
+ }
+ }
+ else
+ {
+ /* some sort of replacement */
+ if (avp >= &npvp[MAXATOM])
+ {
+ toolong:
+ syserr("554 rewrite: expansion too long");
+ return EX_DATAERR;
+ }
+ if ((*rp & 0377) != MACRODEXPAND)
+ {
+ /* vanilla replacement */
+ *avp++ = rp;
+ }
+ else
+ {
+ /* $&x replacement */
+ char *mval = macvalue(rp[1], e);
+ char **xpvp;
+ int trsize = 0;
+ static size_t pvpb1_size = 0;
+ static char **pvpb1 = NULL;
+ char pvpbuf[PSBUFSIZE];
+
+ if (tTd(21, 2))
+ printf("rewrite: RHS $&%s => \"%s\"\n",
+ macname(rp[1]),
+ mval == NULL ? "(NULL)" : mval);
+ if (mval == NULL || *mval == '\0')
+ continue;
+
+ /* save the remainder of the input */
+ for (xpvp = pvp; *xpvp != NULL; xpvp++)
+ trsize += sizeof *xpvp;
+ if (trsize > pvpb1_size)
+ {
+ if (pvpb1 != NULL)
+ free(pvpb1);
+ pvpb1 = (char **)xalloc(trsize);
+ pvpb1_size = trsize;
+ }
+
+ bcopy((char *) pvp, (char *) pvpb1, trsize);
+
+ /* scan the new replacement */
+ xpvp = prescan(mval, '\0', pvpbuf,
+ sizeof pvpbuf, NULL, NULL);
+ if (xpvp == NULL)
+ {
+ /* prescan pre-printed error */
+ return EX_DATAERR;
+ }
+
+ /* insert it into the output stream */
+ while (*xpvp != NULL)
+ {
+ if (tTd(21, 19))
+ printf(" ... %s\n", *xpvp);
+ *avp++ = newstr(*xpvp);
+ if (avp >= &npvp[MAXATOM])
+ goto toolong;
+ xpvp++;
+ }
+ if (tTd(21, 19))
+ printf(" ... DONE\n");
+
+ /* restore the old trailing input */
+ bcopy((char *) pvpb1, (char *) pvp, trsize);
+ }
+ }
+ }
+ *avp++ = NULL;
+
+ /*
+ ** Check for any hostname/keyword lookups.
+ */
+
+ for (rvp = npvp; *rvp != NULL; rvp++)
+ {
+ char **hbrvp;
+ char **xpvp;
+ int trsize;
+ char *replac;
+ int endtoken;
+ STAB *map;
+ char *mapname;
+ char **key_rvp;
+ char **arg_rvp;
+ char **default_rvp;
+ char buf[MAXNAME + 1];
+ char *pvpb1[MAXATOM + 1];
+ char *argvect[10];
+ char pvpbuf[PSBUFSIZE];
+ char *nullpvp[1];
+ extern char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
+
+ if ((**rvp & 0377) != HOSTBEGIN &&
+ (**rvp & 0377) != LOOKUPBEGIN)
+ continue;
+
+ /*
+ ** Got a hostname/keyword lookup.
+ **
+ ** This could be optimized fairly easily.
+ */
+
+ hbrvp = rvp;
+ if ((**rvp & 0377) == HOSTBEGIN)
+ {
+ endtoken = HOSTEND;
+ mapname = "host";
+ }
+ else
+ {
+ endtoken = LOOKUPEND;
+ mapname = *++rvp;
+ }
+ map = stab(mapname, ST_MAP, ST_FIND);
+ if (map == NULL)
+ syserr("554 rewrite: map %s not found", mapname);
+
+ /* extract the match part */
+ key_rvp = ++rvp;
+ default_rvp = NULL;
+ arg_rvp = argvect;
+ xpvp = NULL;
+ replac = pvpbuf;
+ while (*rvp != NULL && (**rvp & 0377) != endtoken)
+ {
+ int nodetype = **rvp & 0377;
+
+ if (nodetype != CANONHOST && nodetype != CANONUSER)
+ {
+ rvp++;
+ continue;
+ }
+
+ *rvp++ = NULL;
+
+ if (xpvp != NULL)
+ {
+ cataddr(xpvp, NULL, replac,
+ &pvpbuf[sizeof pvpbuf] - replac,
+ '\0');
+ *++arg_rvp = replac;
+ replac += strlen(replac) + 1;
+ xpvp = NULL;
+ }
+ switch (nodetype)
+ {
+ case CANONHOST:
+ xpvp = rvp;
+ break;
+
+ case CANONUSER:
+ default_rvp = rvp;
+ break;
+ }
+ }
+ if (*rvp != NULL)
+ *rvp++ = NULL;
+ if (xpvp != NULL)
+ {
+ cataddr(xpvp, NULL, replac,
+ &pvpbuf[sizeof pvpbuf] - replac,
+ '\0');
+ *++arg_rvp = replac;
+ }
+ *++arg_rvp = NULL;
+
+ /* save the remainder of the input string */
+ trsize = (int) (avp - rvp + 1) * sizeof *rvp;
+ bcopy((char *) rvp, (char *) pvpb1, trsize);
+
+ /* look it up */
+ cataddr(key_rvp, NULL, buf, sizeof buf, '\0');
+ argvect[0] = buf;
+ replac = map_lookup(map, buf, argvect, &rstat, e);
+
+ /* if no replacement, use default */
+ if (replac == NULL && default_rvp != NULL)
+ {
+ /* create the default */
+ cataddr(default_rvp, NULL, buf, sizeof buf, '\0');
+ replac = buf;
+ }
+
+ if (replac == NULL)
+ {
+ xpvp = key_rvp;
+ }
+ else if (*replac == '\0')
+ {
+ /* null replacement */
+ nullpvp[0] = NULL;
+ xpvp = nullpvp;
+ }
+ else
+ {
+ /* scan the new replacement */
+ xpvp = prescan(replac, '\0', pvpbuf,
+ sizeof pvpbuf, NULL, NULL);
+ if (xpvp == NULL)
+ {
+ /* prescan already printed error */
+ return EX_DATAERR;
+ }
+ }
+
+ /* append it to the token list */
+ for (avp = hbrvp; *xpvp != NULL; xpvp++)
+ {
+ *avp++ = newstr(*xpvp);
+ if (avp >= &npvp[MAXATOM])
+ goto toolong;
+ }
+
+ /* restore the old trailing information */
+ rvp = avp - 1;
+ for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
+ if (avp >= &npvp[MAXATOM])
+ goto toolong;
+ }
+
+ /*
+ ** Check for subroutine calls.
+ */
+
+ stat = callsubr(npvp, reclevel, e);
+ if (rstat == EX_OK || stat == EX_TEMPFAIL)
+ rstat = stat;
+
+ /* copy vector back into original space. */
+ for (avp = npvp; *avp++ != NULL;)
+ continue;
+ bcopy((char *) npvp, (char *) pvp,
+ (int) (avp - npvp) * sizeof *avp);
+
+ if (tTd(21, 4))
+ {
+ printf("rewritten as:");
+ printav(pvp);
+ }
+ }
+
+ if (OpMode == MD_TEST || tTd(21, 1))
+ {
+ printf("rewrite: ruleset %3d returns:", ruleset);
+ printav(pvp);
+ }
+
+ return rstat;
+}
+ /*
+** CALLSUBR -- call subroutines in rewrite vector
+**
+** Parameters:
+** pvp -- pointer to token vector.
+** reclevel -- the current recursion level.
+** e -- the current envelope.
+**
+** Returns:
+** The status from the subroutine call.
+**
+** Side Effects:
+** pvp is modified.
+*/
+
+int
+callsubr(pvp, reclevel, e)
+ char **pvp;
+ int reclevel;
+ ENVELOPE *e;
+{
+ char **avp;
+ char **rvp;
+ register int i;
+ int subr;
+ int stat;
+ int rstat = EX_OK;
+ char *tpvp[MAXATOM + 1];
+
+ for (avp = pvp; *avp != NULL; avp++)
+ {
+ if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)
+ {
+ stripquotes(avp[1]);
+ subr = strtorwset(avp[1], NULL, ST_FIND);
+ if (subr < 0)
+ {
+ syserr("Unknown ruleset %s", avp[1]);
+ return EX_CONFIG;
+ }
+
+ if (tTd(21, 3))
+ printf("-----callsubr %s (%d)\n", avp[1], subr);
+
+ /*
+ ** Take care of possible inner calls first.
+ ** use a full size temporary buffer to avoid
+ ** overflows in rewrite, but strip off the
+ ** subroutine call.
+ */
+
+ for (i = 2; avp[i] != NULL; i++)
+ tpvp[i - 2] = avp[i];
+ tpvp[i - 2] = NULL;
+
+ stat = callsubr(tpvp, reclevel, e);
+ if (rstat == EX_OK || stat == EX_TEMPFAIL)
+ rstat = stat;
+
+ /*
+ ** Now we need to call the ruleset specified for
+ ** the subroutine. we can do this with the
+ ** temporary buffer that we set up earlier,
+ ** since it has all the data we want to rewrite.
+ */
+
+ stat = rewrite(tpvp, subr, reclevel, e);
+ if (rstat == EX_OK || stat == EX_TEMPFAIL)
+ rstat = stat;
+
+ /*
+ ** Find length of tpvp and current offset into
+ ** pvp, if the total is greater than MAXATOM,
+ ** then it would overflow the buffer if we copied
+ ** it back in to pvp, in which case we throw a
+ ** fit.
+ */
+
+ for (rvp = tpvp; *rvp != NULL; rvp++)
+ continue;
+ if (((rvp - tpvp) + (avp - pvp)) > MAXATOM)
+ {
+ syserr("554 callsubr: expansion too long");
+ return EX_DATAERR;
+ }
+
+ /*
+ ** Now we can copy the rewritten code over
+ ** the initial subroutine call in the buffer.
+ */
+
+ for (i = 0; tpvp[i] != NULL; i++)
+ avp[i] = tpvp[i];
+ avp[i] = NULL;
+
+ /*
+ ** If we got this far, we've processed the left
+ ** most subroutine, and recursively called ourselves
+ ** to handle any other subroutines. We're done.
+ */
+
+ break;
+ }
+ }
+ return rstat;
+}
+ /*
+** MAP_LOOKUP -- do lookup in map
+**
+** Parameters:
+** map -- the map to use for the lookup.
+** key -- the key to look up.
+** argvect -- arguments to pass to the map lookup.
+** pstat -- a pointer to an integer in which to store the
+** status from the lookup.
+** e -- the current envelope.
+**
+** Returns:
+** The result of the lookup.
+** NULL -- if there was no data for the given key.
+*/
+
+char *
+map_lookup(map, key, argvect, pstat, e)
+ STAB *map;
+ char key[];
+ char **argvect;
+ int *pstat;
+ ENVELOPE *e;
+{
+ auto int stat = EX_OK;
+ char *replac;
+
+ if (e->e_sendmode == SM_DEFER)
+ {
+ /* don't do any map lookups */
+ if (tTd(60, 1))
+ printf("map_lookup(%s, %s) => DEFERRED\n",
+ map->s_name, key);
+ *pstat = EX_TEMPFAIL;
+ return NULL;
+ }
+ if (map == NULL || !bitset(MF_OPEN, map->s_map.map_mflags))
+ return NULL;
+
+ if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags))
+ stripquotes(key);
+
+ /* XXX should try to auto-open the map here */
+
+ if (tTd(60, 1))
+ printf("map_lookup(%s, %s) => ",
+ map->s_name, key);
+ replac = (*map->s_map.map_class->map_lookup)(&map->s_map,
+ key, argvect, &stat);
+ if (tTd(60, 1))
+ printf("%s (%d)\n",
+ replac != NULL ? replac : "NOT FOUND",
+ stat);
+
+ /* should recover if stat == EX_TEMPFAIL */
+ if (stat == EX_TEMPFAIL && !bitset(MF_NODEFER, map->s_map.map_mflags))
+ {
+ *pstat = EX_TEMPFAIL;
+ if (tTd(60, 1))
+ printf("map_lookup(%s, %s) tempfail: errno=%d\n",
+ map->s_name, key, errno);
+ if (e->e_message == NULL)
+ {
+ char mbuf[320];
+
+ snprintf(mbuf, sizeof mbuf,
+ "%.80s map: lookup (%s): deferred",
+ map->s_name,
+ shortenstring(key, MAXSHORTSTR));
+ e->e_message = newstr(mbuf);
+ }
+ }
+ if (stat == EX_TEMPFAIL && map->s_map.map_tapp != NULL)
+ {
+ size_t i = strlen(key) + strlen(map->s_map.map_tapp) + 1;
+ static char *rwbuf = NULL;
+ static size_t rwbuflen = 0;
+
+ if (i > rwbuflen)
+ {
+ if (rwbuf != NULL)
+ free(rwbuf);
+ rwbuflen = i;
+ rwbuf = (char *) xalloc(rwbuflen);
+ }
+ snprintf(rwbuf, rwbuflen, "%s%s", key, map->s_map.map_tapp);
+ if (tTd(60, 4))
+ printf("map_lookup tempfail: returning \"%s\"\n",
+ rwbuf);
+ return rwbuf;
+ }
+ return replac;
+}
+ /*
+** BUILDADDR -- build address from token vector.
+**
+** Parameters:
+** tv -- token vector.
+** a -- pointer to address descriptor to fill.
+** If NULL, one will be allocated.
+** flags -- info regarding whether this is a sender or
+** a recipient.
+** e -- the current envelope.
+**
+** Returns:
+** NULL if there was an error.
+** 'a' otherwise.
+**
+** Side Effects:
+** fills in 'a'
+*/
+
+struct errcodes
+{
+ char *ec_name; /* name of error code */
+ int ec_code; /* numeric code */
+} ErrorCodes[] =
+{
+ { "usage", EX_USAGE },
+ { "nouser", EX_NOUSER },
+ { "nohost", EX_NOHOST },
+ { "unavailable", EX_UNAVAILABLE },
+ { "software", EX_SOFTWARE },
+ { "tempfail", EX_TEMPFAIL },
+ { "protocol", EX_PROTOCOL },
+#ifdef EX_CONFIG
+ { "config", EX_CONFIG },
+#endif
+ { NULL, EX_UNAVAILABLE }
+};
+
+ADDRESS *
+buildaddr(tv, a, flags, e)
+ register char **tv;
+ register ADDRESS *a;
+ int flags;
+ register ENVELOPE *e;
+{
+ struct mailer **mp;
+ register struct mailer *m;
+ register char *p;
+ char *mname;
+ char **hostp;
+ char hbuf[MAXNAME + 1];
+ static MAILER discardmailer;
+ static MAILER errormailer;
+ static char *discardargv[] = { "DISCARD", NULL };
+ static char *errorargv[] = { "ERROR", NULL };
+ static char ubuf[MAXNAME + 1];
+
+ if (tTd(24, 5))
+ {
+ printf("buildaddr, flags=%x, tv=", flags);
+ printav(tv);
+ }
+
+ if (a == NULL)
+ a = (ADDRESS *) xalloc(sizeof *a);
+ bzero((char *) a, sizeof *a);
+
+ /* set up default error return flags */
+ a->q_flags |= DefaultNotify;
+
+ if (discardmailer.m_name == NULL)
+ {
+ /* initialize the discard mailer */
+ discardmailer.m_name = "*discard*";
+ discardmailer.m_mailer = "DISCARD";
+ discardmailer.m_argv = discardargv;
+ }
+
+ /* figure out what net/mailer to use */
+ if (*tv == NULL || (**tv & 0377) != CANONNET)
+ {
+ syserr("554 buildaddr: no mailer in parsed address");
+badaddr:
+ a->q_flags |= QBADADDR;
+ a->q_mailer = &errormailer;
+ if (errormailer.m_name == NULL)
+ {
+ /* initialize the bogus mailer */
+ errormailer.m_name = "*error*";
+ errormailer.m_mailer = "ERROR";
+ errormailer.m_argv = errorargv;
+ }
+ return a;
+ }
+ mname = *++tv;
+
+ /* extract host and user portions */
+ if (*++tv != NULL && (**tv & 0377) == CANONHOST)
+ hostp = ++tv;
+ else
+ hostp = NULL;
+ while (*tv != NULL && (**tv & 0377) != CANONUSER)
+ tv++;
+ if (*tv == NULL)
+ {
+ syserr("554 buildaddr: no user");
+ goto badaddr;
+ }
+ if (tv == hostp)
+ hostp = NULL;
+ else if (hostp != NULL)
+ cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
+ cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');
+
+ /* save away the host name */
+ if (strcasecmp(mname, "error") == 0)
+ {
+ if (hostp != NULL)
+ {
+ register struct errcodes *ep;
+
+ if (strchr(hbuf, '.') != NULL)
+ {
+ extern int dsntoexitstat __P((char *));
+
+ a->q_status = newstr(hbuf);
+ setstat(dsntoexitstat(hbuf));
+ }
+ else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
+ {
+ setstat(atoi(hbuf));
+ }
+ else
+ {
+ for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
+ if (strcasecmp(ep->ec_name, hbuf) == 0)
+ break;
+ setstat(ep->ec_code);
+ }
+ }
+ else
+ setstat(EX_UNAVAILABLE);
+ stripquotes(ubuf);
+ if (isascii(ubuf[0]) && isdigit(ubuf[0]) &&
+ isascii(ubuf[1]) && isdigit(ubuf[1]) &&
+ isascii(ubuf[2]) && isdigit(ubuf[2]) &&
+ ubuf[3] == ' ')
+ {
+ char fmt[10];
+
+ strncpy(fmt, ubuf, 3);
+ strcpy(&fmt[3], " %s");
+ usrerr(fmt, ubuf + 4);
+
+ /*
+ ** If this is a 4xx code and we aren't running
+ ** SMTP on our input, bounce this message;
+ ** otherwise it disappears without a trace.
+ */
+
+ if (fmt[0] == '4' && OpMode != MD_SMTP &&
+ OpMode != MD_DAEMON)
+ {
+ e->e_flags |= EF_FATALERRS;
+ }
+ }
+ else
+ {
+ usrerr("553 %s", ubuf);
+ }
+ goto badaddr;
+ }
+
+ for (mp = Mailer; (m = *mp++) != NULL; )
+ {
+ if (strcasecmp(m->m_name, mname) == 0)
+ break;
+ }
+ if (m == NULL)
+ {
+ syserr("554 buildaddr: unknown mailer %s", mname);
+ goto badaddr;
+ }
+ a->q_mailer = m;
+
+ /* figure out what host (if any) */
+ if (hostp == NULL)
+ {
+ if (!bitnset(M_LOCALMAILER, m->m_flags))
+ {
+ syserr("554 buildaddr: no host");
+ goto badaddr;
+ }
+ a->q_host = NULL;
+ }
+ else
+ a->q_host = newstr(hbuf);
+
+ /* figure out the user */
+ p = ubuf;
+ if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
+ {
+ p++;
+ tv++;
+ a->q_flags |= QNOTREMOTE;
+ }
+
+ /* do special mapping for local mailer */
+ if (*p == '"')
+ p++;
+ if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
+ a->q_mailer = m = ProgMailer;
+ else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
+ a->q_mailer = m = FileMailer;
+ else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
+ {
+ /* may be :include: */
+ stripquotes(ubuf);
+ if (strncasecmp(ubuf, ":include:", 9) == 0)
+ {
+ /* if :include:, don't need further rewriting */
+ a->q_mailer = m = InclMailer;
+ a->q_user = newstr(&ubuf[9]);
+ return a;
+ }
+ }
+
+ /* rewrite according recipient mailer rewriting rules */
+ define('h', a->q_host, e);
+ if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
+ {
+ /* sender addresses done later */
+ (void) rewrite(tv, 2, 0, e);
+ if (m->m_re_rwset > 0)
+ (void) rewrite(tv, m->m_re_rwset, 0, e);
+ }
+ (void) rewrite(tv, 4, 0, e);
+
+ /* save the result for the command line/RCPT argument */
+ cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
+ a->q_user = ubuf;
+
+ /*
+ ** Do mapping to lower case as requested by mailer
+ */
+
+ if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
+ makelower(a->q_host);
+ if (!bitnset(M_USR_UPPER, m->m_flags))
+ makelower(a->q_user);
+
+ if (tTd(24, 6))
+ {
+ printf("buildaddr => ");
+ printaddr(a, FALSE);
+ }
+ return a;
+}
+ /*
+** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
+**
+** Parameters:
+** pvp -- parameter vector to rebuild.
+** evp -- last parameter to include. Can be NULL to
+** use entire pvp.
+** buf -- buffer to build the string into.
+** sz -- size of buf.
+** spacesub -- the space separator character; if null,
+** use SpaceSub.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Destroys buf.
+*/
+
+void
+cataddr(pvp, evp, buf, sz, spacesub)
+ char **pvp;
+ char **evp;
+ char *buf;
+ register int sz;
+ int spacesub;
+{
+ bool oatomtok = FALSE;
+ bool natomtok = FALSE;
+ register int i;
+ register char *p;
+
+ if (spacesub == '\0')
+ spacesub = SpaceSub;
+
+ if (pvp == NULL)
+ {
+ (void) strcpy(buf, "");
+ return;
+ }
+ p = buf;
+ sz -= 2;
+ while (*pvp != NULL && (i = strlen(*pvp)) < sz)
+ {
+ natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
+ if (oatomtok && natomtok)
+ *p++ = spacesub;
+ (void) strcpy(p, *pvp);
+ oatomtok = natomtok;
+ p += i;
+ sz -= i + 1;
+ if (pvp++ == evp)
+ break;
+ }
+ *p = '\0';
+}
+ /*
+** SAMEADDR -- Determine if two addresses are the same
+**
+** This is not just a straight comparison -- if the mailer doesn't
+** care about the host we just ignore it, etc.
+**
+** Parameters:
+** a, b -- pointers to the internal forms to compare.
+**
+** Returns:
+** TRUE -- they represent the same mailbox.
+** FALSE -- they don't.
+**
+** Side Effects:
+** none.
+*/
+
+bool
+sameaddr(a, b)
+ register ADDRESS *a;
+ register ADDRESS *b;
+{
+ register ADDRESS *ca, *cb;
+
+ /* if they don't have the same mailer, forget it */
+ if (a->q_mailer != b->q_mailer)
+ return (FALSE);
+
+ /* if the user isn't the same, we can drop out */
+ if (strcmp(a->q_user, b->q_user) != 0)
+ return (FALSE);
+
+ /* if we have good uids for both but they differ, these are different */
+ if (a->q_mailer == ProgMailer)
+ {
+ ca = getctladdr(a);
+ cb = getctladdr(b);
+ if (ca != NULL && cb != NULL &&
+ bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
+ ca->q_uid != cb->q_uid)
+ return (FALSE);
+ }
+
+ /* otherwise compare hosts (but be careful for NULL ptrs) */
+ if (a->q_host == b->q_host)
+ {
+ /* probably both null pointers */
+ return (TRUE);
+ }
+ if (a->q_host == NULL || b->q_host == NULL)
+ {
+ /* only one is a null pointer */
+ return (FALSE);
+ }
+ if (strcmp(a->q_host, b->q_host) != 0)
+ return (FALSE);
+
+ return (TRUE);
+}
+ /*
+** PRINTADDR -- print address (for debugging)
+**
+** Parameters:
+** a -- the address to print
+** follow -- follow the q_next chain.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+*/
+
+struct qflags
+{
+ char *qf_name;
+ u_long qf_bit;
+};
+
+struct qflags AddressFlags[] =
+{
+ { "QDONTSEND", QDONTSEND },
+ { "QBADADDR", QBADADDR },
+ { "QGOODUID", QGOODUID },
+ { "QPRIMARY", QPRIMARY },
+ { "QQUEUEUP", QQUEUEUP },
+ { "QSENT", QSENT },
+ { "QNOTREMOTE", QNOTREMOTE },
+ { "QSELFREF", QSELFREF },
+ { "QVERIFIED", QVERIFIED },
+ { "QBOGUSSHELL", QBOGUSSHELL },
+ { "QUNSAFEADDR", QUNSAFEADDR },
+ { "QPINGONSUCCESS", QPINGONSUCCESS },
+ { "QPINGONFAILURE", QPINGONFAILURE },
+ { "QPINGONDELAY", QPINGONDELAY },
+ { "QHASNOTIFY", QHASNOTIFY },
+ { "QRELAYED", QRELAYED },
+ { "QEXPANDED", QEXPANDED },
+ { "QDELIVERED", QDELIVERED },
+ { "QDELAYED", QDELAYED },
+ { "QTHISPASS", QTHISPASS },
+ { "QRCPTOK", QRCPTOK },
+ { NULL }
+};
+
+void
+printaddr(a, follow)
+ register ADDRESS *a;
+ bool follow;
+{
+ register MAILER *m;
+ MAILER pseudomailer;
+ register struct qflags *qfp;
+ bool firstone;
+
+ if (a == NULL)
+ {
+ printf("[NULL]\n");
+ return;
+ }
+
+ while (a != NULL)
+ {
+ printf("%lx=", (u_long) a);
+ (void) fflush(stdout);
+
+ /* find the mailer -- carefully */
+ m = a->q_mailer;
+ if (m == NULL)
+ {
+ m = &pseudomailer;
+ m->m_mno = -1;
+ m->m_name = "NULL";
+ }
+
+ printf("%s:\n\tmailer %d (%s), host `%s'\n",
+ a->q_paddr == NULL ? "<null>" : a->q_paddr,
+ m->m_mno, m->m_name,
+ a->q_host == NULL ? "<null>" : a->q_host);
+ printf("\tuser `%s', ruser `%s'\n",
+ a->q_user,
+ a->q_ruser == NULL ? "<null>" : a->q_ruser);
+ printf("\tnext=%lx, alias %lx, uid %d, gid %d\n",
+ (u_long) a->q_next, (u_long) a->q_alias,
+ (int) a->q_uid, (int) a->q_gid);
+ printf("\tflags=%lx<", a->q_flags);
+ firstone = TRUE;
+ for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
+ {
+ if (!bitset(qfp->qf_bit, a->q_flags))
+ continue;
+ if (!firstone)
+ printf(",");
+ firstone = FALSE;
+ printf("%s", qfp->qf_name);
+ }
+ printf(">\n");
+ printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
+ a->q_owner == NULL ? "(none)" : a->q_owner,
+ a->q_home == NULL ? "(none)" : a->q_home,
+ a->q_fullname == NULL ? "(none)" : a->q_fullname);
+ printf("\torcpt=\"%s\", statmta=%s, status=%s\n",
+ a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
+ a->q_statmta == NULL ? "(none)" : a->q_statmta,
+ a->q_status == NULL ? "(none)" : a->q_status);
+ printf("\trstatus=\"%s\"\n",
+ a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
+ printf("\tspecificity=%d, statdate=%s\n",
+ a->q_specificity, ctime(&a->q_statdate));
+
+ if (!follow)
+ return;
+ a = a->q_next;
+ }
+}
+ /*
+** EMPTYADDR -- return TRUE if this address is empty (``<>'')
+**
+** Parameters:
+** a -- pointer to the address
+**
+** Returns:
+** TRUE -- if this address is "empty" (i.e., no one should
+** ever generate replies to it.
+** FALSE -- if it is a "regular" (read: replyable) address.
+*/
+
+bool
+emptyaddr(a)
+ register ADDRESS *a;
+{
+ return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
+ a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
+}
+ /*
+** REMOTENAME -- return the name relative to the current mailer
+**
+** Parameters:
+** name -- the name to translate.
+** m -- the mailer that we want to do rewriting relative
+** to.
+** flags -- fine tune operations.
+** pstat -- pointer to status word.
+** e -- the current envelope.
+**
+** Returns:
+** the text string representing this address relative to
+** the receiving mailer.
+**
+** Side Effects:
+** none.
+**
+** Warnings:
+** The text string returned is tucked away locally;
+** copy it if you intend to save it.
+*/
+
+char *
+remotename(name, m, flags, pstat, e)
+ char *name;
+ struct mailer *m;
+ int flags;
+ int *pstat;
+ register ENVELOPE *e;
+{
+ register char **pvp;
+ char *fancy;
+ char *oldg = macvalue('g', e);
+ int rwset;
+ static char buf[MAXNAME + 1];
+ char lbuf[MAXNAME + 1];
+ char pvpbuf[PSBUFSIZE];
+ extern char *crackaddr __P((char *));
+
+ if (tTd(12, 1))
+ printf("remotename(%s)\n", name);
+
+ /* don't do anything if we are tagging it as special */
+ if (bitset(RF_SENDERADDR, flags))
+ rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
+ : m->m_se_rwset;
+ else
+ rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
+ : m->m_re_rwset;
+ if (rwset < 0)
+ return (name);
+
+ /*
+ ** Do a heuristic crack of this name to extract any comment info.
+ ** This will leave the name as a comment and a $g macro.
+ */
+
+ if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
+ fancy = "\201g";
+ else
+ fancy = crackaddr(name);
+
+ /*
+ ** Turn the name into canonical form.
+ ** Normally this will be RFC 822 style, i.e., "user@domain".
+ ** If this only resolves to "user", and the "C" flag is
+ ** specified in the sending mailer, then the sender's
+ ** domain will be appended.
+ */
+
+ pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
+ if (pvp == NULL)
+ return (name);
+ if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
+ *pstat = EX_TEMPFAIL;
+ if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
+ {
+ /* append from domain to this address */
+ register char **pxp = pvp;
+
+ /* see if there is an "@domain" in the current name */
+ while (*pxp != NULL && strcmp(*pxp, "@") != 0)
+ pxp++;
+ if (*pxp == NULL)
+ {
+ /* no.... append the "@domain" from the sender */
+ register char **qxq = e->e_fromdomain;
+
+ while ((*pxp++ = *qxq++) != NULL)
+ continue;
+ if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
+ *pstat = EX_TEMPFAIL;
+ }
+ }
+
+ /*
+ ** Do more specific rewriting.
+ ** Rewrite using ruleset 1 or 2 depending on whether this is
+ ** a sender address or not.
+ ** Then run it through any receiving-mailer-specific rulesets.
+ */
+
+ if (bitset(RF_SENDERADDR, flags))
+ {
+ if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
+ *pstat = EX_TEMPFAIL;
+ }
+ else
+ {
+ if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
+ *pstat = EX_TEMPFAIL;
+ }
+ if (rwset > 0)
+ {
+ if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
+ *pstat = EX_TEMPFAIL;
+ }
+
+ /*
+ ** Do any final sanitation the address may require.
+ ** This will normally be used to turn internal forms
+ ** (e.g., user@host.LOCAL) into external form. This
+ ** may be used as a default to the above rules.
+ */
+
+ if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
+ *pstat = EX_TEMPFAIL;
+
+ /*
+ ** Now restore the comment information we had at the beginning.
+ */
+
+ cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
+ define('g', lbuf, e);
+
+ /* need to make sure route-addrs have <angle brackets> */
+ if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
+ expand("<\201g>", buf, sizeof buf, e);
+ else
+ expand(fancy, buf, sizeof buf, e);
+
+ define('g', oldg, e);
+
+ if (tTd(12, 1))
+ printf("remotename => `%s'\n", buf);
+ return (buf);
+}
+ /*
+** MAPLOCALUSER -- run local username through ruleset 5 for final redirection
+**
+** Parameters:
+** a -- the address to map (but just the user name part).
+** sendq -- the sendq in which to install any replacement
+** addresses.
+** aliaslevel -- the alias nesting depth.
+** e -- the envelope.
+**
+** Returns:
+** none.
+*/
+
+#define Q_COPYFLAGS (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
+ Q_PINGFLAGS|QHASNOTIFY|\
+ QRELAYED|QEXPANDED|QDELIVERED|QDELAYED)
+
+void
+maplocaluser(a, sendq, aliaslevel, e)
+ register ADDRESS *a;
+ ADDRESS **sendq;
+ int aliaslevel;
+ ENVELOPE *e;
+{
+ register char **pvp;
+ register ADDRESS *a1 = NULL;
+ auto char *delimptr;
+ char pvpbuf[PSBUFSIZE];
+
+ if (tTd(29, 1))
+ {
+ printf("maplocaluser: ");
+ printaddr(a, FALSE);
+ }
+ pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
+ if (pvp == NULL)
+ {
+ if (tTd(29, 9))
+ printf("maplocaluser: cannot prescan %s\n", a->q_user);
+ return;
+ }
+
+ define('h', a->q_host, e);
+ define('u', a->q_user, e);
+ define('z', a->q_home, e);
+
+ if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL)
+ {
+ if (tTd(29, 9))
+ printf("maplocaluser: rewrite tempfail\n");
+ a->q_flags |= QQUEUEUP;
+ a->q_status = "4.4.3";
+ return;
+ }
+ if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
+ {
+ if (tTd(29, 9))
+ printf("maplocaluser: doesn't resolve\n");
+ return;
+ }
+
+ /* if non-null, mailer destination specified -- has it changed? */
+ a1 = buildaddr(pvp, NULL, 0, e);
+ if (a1 == NULL || sameaddr(a, a1))
+ {
+ if (tTd(29, 9))
+ printf("maplocaluser: address unchanged\n");
+ if (a1 != NULL)
+ free(a1);
+ return;
+ }
+
+ /* make new address take on flags and print attributes of old */
+ a1->q_flags &= ~Q_COPYFLAGS;
+ a1->q_flags |= a->q_flags & Q_COPYFLAGS;
+ a1->q_paddr = a->q_paddr;
+
+ /* mark old address as dead; insert new address */
+ a->q_flags |= QDONTSEND;
+ if (tTd(29, 5))
+ {
+ printf("maplocaluser: QDONTSEND ");
+ printaddr(a, FALSE);
+ }
+ a1->q_alias = a;
+ allocaddr(a1, RF_COPYALL, a->q_paddr);
+ (void) recipient(a1, sendq, aliaslevel, e);
+}
+ /*
+** DEQUOTE_INIT -- initialize dequote map
+**
+** This is a no-op.
+**
+** Parameters:
+** map -- the internal map structure.
+** args -- arguments.
+**
+** Returns:
+** TRUE.
+*/
+
+bool
+dequote_init(map, args)
+ MAP *map;
+ char *args;
+{
+ register char *p = args;
+
+ map->map_mflags |= MF_KEEPQUOTES;
+ for (;;)
+ {
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p != '-')
+ break;
+ switch (*++p)
+ {
+ case 'a':
+ map->map_app = ++p;
+ break;
+
+ case 's':
+ map->map_coldelim = *++p;
+ break;
+ }
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p = '\0';
+ }
+ if (map->map_app != NULL)
+ map->map_app = newstr(map->map_app);
+
+ return TRUE;
+}
+ /*
+** DEQUOTE_MAP -- unquote an address
+**
+** Parameters:
+** map -- the internal map structure (ignored).
+** name -- the name to dequote.
+** av -- arguments (ignored).
+** statp -- pointer to status out-parameter.
+**
+** Returns:
+** NULL -- if there were no quotes, or if the resulting
+** unquoted buffer would not be acceptable to prescan.
+** else -- The dequoted buffer.
+*/
+
+/* ARGSUSED2 */
+char *
+dequote_map(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ register char *p;
+ register char *q;
+ register char c;
+ int anglecnt = 0;
+ int cmntcnt = 0;
+ int quotecnt = 0;
+ int spacecnt = 0;
+ bool quotemode = FALSE;
+ bool bslashmode = FALSE;
+ char spacesub = map->map_coldelim;
+
+ for (p = q = name; (c = *p++) != '\0'; )
+ {
+ if (bslashmode)
+ {
+ bslashmode = FALSE;
+ *q++ = c;
+ continue;
+ }
+
+ if (c == ' ' && spacesub != '\0')
+ c = spacesub;
+
+ switch (c)
+ {
+ case '\\':
+ bslashmode = TRUE;
+ break;
+
+ case '(':
+ cmntcnt++;
+ break;
+
+ case ')':
+ if (cmntcnt-- <= 0)
+ return NULL;
+ break;
+
+ case ' ':
+ spacecnt++;
+ break;
+ }
+
+ if (cmntcnt > 0)
+ {
+ *q++ = c;
+ continue;
+ }
+
+ switch (c)
+ {
+ case '"':
+ quotemode = !quotemode;
+ quotecnt++;
+ continue;
+
+ case '<':
+ anglecnt++;
+ break;
+
+ case '>':
+ if (anglecnt-- <= 0)
+ return NULL;
+ break;
+ }
+ *q++ = c;
+ }
+
+ if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
+ quotemode || quotecnt <= 0 || spacecnt != 0)
+ return NULL;
+ *q++ = '\0';
+ return map_rewrite(map, name, strlen(name), NULL);
+}
+ /*
+** RSCHECK -- check string(s) for validity using rewriting sets
+**
+** Parameters:
+** rwset -- the rewriting set to use.
+** p1 -- the first string to check.
+** p2 -- the second string to check -- may be null.
+** e -- the current envelope.
+**
+** Returns:
+** EX_OK -- if the rwset doesn't resolve to $#error
+** else -- the failure status (message printed)
+*/
+
+int
+rscheck(rwset, p1, p2, e)
+ char *rwset;
+ char *p1;
+ char *p2;
+ ENVELOPE *e;
+{
+ char *buf;
+ int bufsize;
+ int saveexitstat;
+ int rstat = EX_OK;
+ char **pvp;
+ int rsno;
+ bool discard = FALSE;
+ auto ADDRESS a1;
+ bool saveQuickAbort = QuickAbort;
+ bool saveSuprErrs = SuprErrs;
+ char buf0[MAXLINE];
+ char pvpbuf[PSBUFSIZE];
+ extern char MsgBuf[];
+
+ if (tTd(48, 2))
+ printf("rscheck(%s, %s, %s)\n", rwset, p1,
+ p2 == NULL ? "(NULL)" : p2);
+
+ rsno = strtorwset(rwset, NULL, ST_FIND);
+ if (rsno < 0)
+ return EX_OK;
+
+ if (p2 != NULL)
+ {
+ bufsize = strlen(p1) + strlen(p2) + 2;
+ if (bufsize > sizeof buf0)
+ buf = xalloc(bufsize);
+ else
+ {
+ buf = buf0;
+ bufsize = sizeof buf0;
+ }
+ (void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
+ }
+ else
+ {
+ bufsize = strlen(p1) + 1;
+ if (bufsize > sizeof buf0)
+ buf = xalloc(bufsize);
+ else
+ {
+ buf = buf0;
+ bufsize = sizeof buf0;
+ }
+ (void) snprintf(buf, bufsize, "%s", p1);
+ }
+ SuprErrs = TRUE;
+ QuickAbort = FALSE;
+ pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
+ SuprErrs = saveSuprErrs;
+ if (pvp == NULL)
+ {
+ if (tTd(48, 2))
+ printf("rscheck: cannot prescan input\n");
+/*
+ syserr("rscheck: cannot prescan input: \"%s\"",
+ shortenstring(buf, MAXSHORTSTR));
+ rstat = EX_DATAERR;
+*/
+ goto finis;
+ }
+ (void) rewrite(pvp, rsno, 0, e);
+ if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
+ pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
+ strcmp(pvp[1], "discard") != 0))
+ {
+ goto finis;
+ }
+
+ if (strcmp(pvp[1], "discard") == 0)
+ {
+ if (tTd(48, 2))
+ printf("rscheck: discard mailer selected\n");
+ e->e_flags |= EF_DISCARD;
+ discard = TRUE;
+ }
+ else
+ {
+ int savelogusrerrs = LogUsrErrs;
+ static bool logged = FALSE;
+
+ /* got an error -- process it */
+ saveexitstat = ExitStat;
+ LogUsrErrs = FALSE;
+ (void) buildaddr(pvp, &a1, 0, e);
+ LogUsrErrs = savelogusrerrs;
+ rstat = ExitStat;
+ ExitStat = saveexitstat;
+ if (!logged)
+ {
+ markstats(e, &a1, TRUE);
+ logged = TRUE;
+ }
+ }
+
+ if (LogLevel >= 4)
+ {
+ char *relay;
+ char *p;
+ char lbuf[MAXLINE];
+
+ p = lbuf;
+ if (p2 != NULL)
+ {
+ snprintf(p, SPACELEFT(lbuf, p),
+ ", arg2=%s",
+ p2);
+ p += strlen(p);
+ }
+ if ((relay = macvalue('_', e)) != NULL)
+ {
+ snprintf(p, SPACELEFT(lbuf, p),
+ ", relay=%s", relay);
+ p += strlen(p);
+ }
+ *p = '\0';
+ if (discard)
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "ruleset=%s, arg1=%s%s, discard",
+ rwset, p1, lbuf);
+ else
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "ruleset=%s, arg1=%s%s, reject=%s",
+ rwset, p1, lbuf, MsgBuf);
+ }
+
+ finis:
+ /* clean up */
+ QuickAbort = saveQuickAbort;
+ setstat(rstat);
+ if (buf != buf0)
+ free(buf);
+
+ if (rstat != EX_OK && QuickAbort)
+ longjmp(TopFrame, 2);
+ return rstat;
+}
diff --git a/contrib/sendmail/src/pathnames.h b/contrib/sendmail/src/pathnames.h
new file mode 100644
index 0000000..e10387e
--- /dev/null
+++ b/contrib/sendmail/src/pathnames.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. 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.
+ *
+ *
+ * @(#)pathnames.h 8.8 (Berkeley) 5/19/98
+ */
+
+#ifndef _PATH_SENDMAILCF
+# if defined(USE_VENDOR_CF_PATH) && defined(_PATH_VENDOR_CF)
+# define _PATH_SENDMAILCF _PATH_VENDOR_CF
+# else
+# define _PATH_SENDMAILCF "/etc/sendmail.cf"
+# endif
+#endif
+
+#ifndef _PATH_SENDMAILPID
+# ifdef BSD4_4
+# define _PATH_SENDMAILPID "/var/run/sendmail.pid"
+# else
+# define _PATH_SENDMAILPID "/etc/sendmail.pid"
+# endif
+#endif
+
+#ifndef _PATH_HOSTS
+# define _PATH_HOSTS "/etc/hosts"
+#endif
diff --git a/contrib/sendmail/src/queue.c b/contrib/sendmail/src/queue.c
new file mode 100644
index 0000000..24b789a
--- /dev/null
+++ b/contrib/sendmail/src/queue.c
@@ -0,0 +1,2402 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+# include "sendmail.h"
+
+#ifndef lint
+#if QUEUE
+static char sccsid[] = "@(#)queue.c 8.202 (Berkeley) 6/15/98 (with queueing)";
+#else
+static char sccsid[] = "@(#)queue.c 8.202 (Berkeley) 6/15/98 (without queueing)";
+#endif
+#endif /* not lint */
+
+# include <errno.h>
+# include <dirent.h>
+
+# if QUEUE
+
+/*
+** Work queue.
+*/
+
+struct work
+{
+ char *w_name; /* name of control file */
+ char *w_host; /* name of recipient host */
+ bool w_lock; /* is message locked? */
+ bool w_tooyoung; /* is it too young to run? */
+ long w_pri; /* priority of message, see below */
+ time_t w_ctime; /* creation time of message */
+ struct work *w_next; /* next in queue */
+};
+
+typedef struct work WORK;
+
+WORK *WorkQ; /* queue of things to be done */
+
+#define QF_VERSION 2 /* version number of this queue format */
+
+extern int orderq __P((bool));
+ /*
+** QUEUEUP -- queue a message up for future transmission.
+**
+** Parameters:
+** e -- the envelope to queue up.
+** announce -- if TRUE, tell when you are queueing up.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** The current request are saved in a control file.
+** The queue file is left locked.
+*/
+
+void
+queueup(e, announce)
+ register ENVELOPE *e;
+ bool announce;
+{
+ char *qf;
+ register FILE *tfp;
+ register HDR *h;
+ register ADDRESS *q;
+ int fd;
+ int i;
+ bool newid;
+ register char *p;
+ MAILER nullmailer;
+ MCI mcibuf;
+ char tf[MAXQFNAME];
+ char buf[MAXLINE];
+ extern void printctladdr __P((ADDRESS *, FILE *));
+
+ /*
+ ** Create control file.
+ */
+
+ newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
+
+ /* if newid, queuename will create a locked qf file in e->lockfp */
+ strcpy(tf, queuename(e, 't'));
+ tfp = e->e_lockfp;
+ if (tfp == NULL)
+ newid = FALSE;
+
+ /* if newid, just write the qf file directly (instead of tf file) */
+ if (!newid)
+ {
+ /* get a locked tf file */
+ for (i = 0; i < 128; i++)
+ {
+ fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
+ if (fd < 0)
+ {
+ if (errno != EEXIST)
+ break;
+ if (LogLevel > 0 && (i % 32) == 0)
+ sm_syslog(LOG_ALERT, e->e_id,
+ "queueup: cannot create %s, uid=%d: %s",
+ tf, geteuid(), errstring(errno));
+ }
+ else
+ {
+ if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB))
+ break;
+ else if (LogLevel > 0 && (i % 32) == 0)
+ sm_syslog(LOG_ALERT, e->e_id,
+ "queueup: cannot lock %s: %s",
+ tf, errstring(errno));
+ close(fd);
+ }
+
+ if ((i % 32) == 31)
+ {
+ /* save the old temp file away */
+ (void) rename(tf, queuename(e, 'T'));
+ }
+ else
+ sleep(i % 32);
+ }
+ if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL)
+ {
+ printopenfds(TRUE);
+ syserr("!queueup: cannot create queue temp file %s, uid=%d",
+ tf, geteuid());
+ }
+ }
+
+ if (tTd(40, 1))
+ printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id,
+ newid ? " (new id)" : "");
+ if (tTd(40, 3))
+ {
+ extern void printenvflags __P((ENVELOPE *));
+
+ printf(" e_flags=");
+ printenvflags(e);
+ }
+ if (tTd(40, 32))
+ {
+ printf(" sendq=");
+ printaddr(e->e_sendqueue, TRUE);
+ }
+ if (tTd(40, 9))
+ {
+ printf(" tfp=");
+ dumpfd(fileno(tfp), TRUE, FALSE);
+ printf(" lockfp=");
+ if (e->e_lockfp == NULL)
+ printf("NULL\n");
+ else
+ dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
+ }
+
+ /*
+ ** If there is no data file yet, create one.
+ */
+
+ if (!bitset(EF_HAS_DF, e->e_flags))
+ {
+ register FILE *dfp = NULL;
+ char dfname[MAXQFNAME];
+ struct stat stbuf;
+
+ strcpy(dfname, queuename(e, 'd'));
+ fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
+ if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL)
+ syserr("!queueup: cannot create data temp file %s, uid=%d",
+ dfname, geteuid());
+ if (fstat(fd, &stbuf) < 0)
+ e->e_dfino = -1;
+ else
+ {
+ e->e_dfdev = stbuf.st_dev;
+ e->e_dfino = stbuf.st_ino;
+ }
+ e->e_flags |= EF_HAS_DF;
+ bzero(&mcibuf, sizeof mcibuf);
+ mcibuf.mci_out = dfp;
+ mcibuf.mci_mailer = FileMailer;
+ (*e->e_putbody)(&mcibuf, e, NULL);
+ (void) xfclose(dfp, "queueup dfp", e->e_id);
+ e->e_putbody = putbody;
+ }
+
+ /*
+ ** Output future work requests.
+ ** Priority and creation time should be first, since
+ ** they are required by orderq.
+ */
+
+ /* output queue version number (must be first!) */
+ fprintf(tfp, "V%d\n", QF_VERSION);
+
+ /* output creation time */
+ fprintf(tfp, "T%ld\n", (long) e->e_ctime);
+
+ /* output last delivery time */
+ fprintf(tfp, "K%ld\n", (long) e->e_dtime);
+
+ /* output number of delivery attempts */
+ fprintf(tfp, "N%d\n", e->e_ntries);
+
+ /* output message priority */
+ fprintf(tfp, "P%ld\n", e->e_msgpriority);
+
+ /* output inode number of data file */
+ /* XXX should probably include device major/minor too */
+ if (e->e_dfino != -1)
+ {
+ if (sizeof e->e_dfino > sizeof(long))
+ fprintf(tfp, "I%d/%d/%s\n",
+ major(e->e_dfdev), minor(e->e_dfdev),
+ quad_to_string(e->e_dfino));
+ else
+ fprintf(tfp, "I%d/%d/%lu\n",
+ major(e->e_dfdev), minor(e->e_dfdev),
+ (unsigned long) e->e_dfino);
+ }
+
+ /* output body type */
+ if (e->e_bodytype != NULL)
+ fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE));
+
+#if _FFR_SAVE_CHARSET
+ if (e->e_charset != NULL)
+ fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE));
+#endif
+
+ /* message from envelope, if it exists */
+ if (e->e_message != NULL)
+ fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE));
+
+ /* send various flag bits through */
+ p = buf;
+ if (bitset(EF_WARNING, e->e_flags))
+ *p++ = 'w';
+ if (bitset(EF_RESPONSE, e->e_flags))
+ *p++ = 'r';
+ if (bitset(EF_HAS8BIT, e->e_flags))
+ *p++ = '8';
+ if (bitset(EF_DELETE_BCC, e->e_flags))
+ *p++ = 'b';
+ if (bitset(EF_RET_PARAM, e->e_flags))
+ *p++ = 'd';
+ if (bitset(EF_NO_BODY_RETN, e->e_flags))
+ *p++ = 'n';
+ *p++ = '\0';
+ if (buf[0] != '\0')
+ fprintf(tfp, "F%s\n", buf);
+
+ /* $r and $s and $_ macro values */
+ if ((p = macvalue('r', e)) != NULL)
+ fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE));
+ if ((p = macvalue('s', e)) != NULL)
+ fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE));
+ if ((p = macvalue('_', e)) != NULL)
+ fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE));
+
+ /* output name of sender */
+ if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
+ p = e->e_sender;
+ else
+ p = e->e_from.q_paddr;
+ fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE));
+
+ /* output ESMTP-supplied "original" information */
+ if (e->e_envid != NULL)
+ fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE));
+
+ /* output list of recipient addresses */
+ printctladdr(NULL, NULL);
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))
+ {
+#if XDEBUG
+ if (bitset(QQUEUEUP, q->q_flags))
+ sm_syslog(LOG_DEBUG, e->e_id,
+ "dropenvelope: q_flags = %x, paddr = %s",
+ q->q_flags, q->q_paddr);
+#endif
+ continue;
+ }
+ printctladdr(q, tfp);
+ if (q->q_orcpt != NULL)
+ fprintf(tfp, "Q%s\n",
+ denlstring(q->q_orcpt, TRUE, FALSE));
+ putc('R', tfp);
+ if (bitset(QPRIMARY, q->q_flags))
+ putc('P', tfp);
+ if (bitset(QHASNOTIFY, q->q_flags))
+ putc('N', tfp);
+ if (bitset(QPINGONSUCCESS, q->q_flags))
+ putc('S', tfp);
+ if (bitset(QPINGONFAILURE, q->q_flags))
+ putc('F', tfp);
+ if (bitset(QPINGONDELAY, q->q_flags))
+ putc('D', tfp);
+ putc(':', tfp);
+ fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE));
+ if (announce)
+ {
+ e->e_to = q->q_paddr;
+ message("queued");
+ if (LogLevel > 8)
+ logdelivery(q->q_mailer, NULL, "queued",
+ NULL, (time_t) 0, e);
+ e->e_to = NULL;
+ }
+ if (tTd(40, 1))
+ {
+ printf("queueing ");
+ printaddr(q, FALSE);
+ }
+ }
+
+ /*
+ ** Output headers for this message.
+ ** Expand macros completely here. Queue run will deal with
+ ** everything as absolute headers.
+ ** All headers that must be relative to the recipient
+ ** can be cracked later.
+ ** We set up a "null mailer" -- i.e., a mailer that will have
+ ** no effect on the addresses as they are output.
+ */
+
+ bzero((char *) &nullmailer, sizeof nullmailer);
+ nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
+ nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
+ nullmailer.m_eol = "\n";
+ bzero(&mcibuf, sizeof mcibuf);
+ mcibuf.mci_mailer = &nullmailer;
+ mcibuf.mci_out = tfp;
+
+ define('g', "\201f", e);
+ for (h = e->e_header; h != NULL; h = h->h_link)
+ {
+ extern bool bitzerop __P((BITMAP));
+
+ /* don't output null headers */
+ if (h->h_value == NULL || h->h_value[0] == '\0')
+ continue;
+
+ /* don't output resent headers on non-resent messages */
+ if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
+ continue;
+
+ /* expand macros; if null, don't output header at all */
+ if (bitset(H_DEFAULT, h->h_flags))
+ {
+ (void) expand(h->h_value, buf, sizeof buf, e);
+ if (buf[0] == '\0')
+ continue;
+ }
+
+ /* output this header */
+ fprintf(tfp, "H");
+
+ /* if conditional, output the set of conditions */
+ if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
+ {
+ int j;
+
+ (void) putc('?', tfp);
+ for (j = '\0'; j <= '\177'; j++)
+ if (bitnset(j, h->h_mflags))
+ (void) putc(j, tfp);
+ (void) putc('?', tfp);
+ }
+
+ /* output the header: expand macros, convert addresses */
+ if (bitset(H_DEFAULT, h->h_flags))
+ {
+ fprintf(tfp, "%s: %s\n",
+ h->h_field,
+ denlstring(buf, FALSE, TRUE));
+ }
+ else if (bitset(H_FROM|H_RCPT, h->h_flags))
+ {
+ bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
+ FILE *savetrace = TrafficLogFile;
+
+ TrafficLogFile = NULL;
+
+ if (bitset(H_FROM, h->h_flags))
+ oldstyle = FALSE;
+
+ commaize(h, h->h_value, oldstyle, &mcibuf, e);
+
+ TrafficLogFile = savetrace;
+ }
+ else
+ {
+ fprintf(tfp, "%s: %s\n",
+ h->h_field,
+ denlstring(h->h_value, FALSE, TRUE));
+ }
+ }
+
+ /*
+ ** Clean up.
+ **
+ ** Write a terminator record -- this is to prevent
+ ** scurrilous crackers from appending any data.
+ */
+
+ fprintf(tfp, ".\n");
+
+ if (fflush(tfp) < 0 ||
+ (SuperSafe && fsync(fileno(tfp)) < 0) ||
+ ferror(tfp))
+ {
+ if (newid)
+ syserr("!552 Error writing control file %s", tf);
+ else
+ syserr("!452 Error writing control file %s", tf);
+ }
+
+ if (!newid)
+ {
+ /* rename (locked) tf to be (locked) qf */
+ qf = queuename(e, 'q');
+ if (rename(tf, qf) < 0)
+ syserr("cannot rename(%s, %s), uid=%d",
+ tf, qf, geteuid());
+
+ /* close and unlock old (locked) qf */
+ if (e->e_lockfp != NULL)
+ (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id);
+ e->e_lockfp = tfp;
+ }
+ else
+ qf = tf;
+ errno = 0;
+ e->e_flags |= EF_INQUEUE;
+
+ /* save log info */
+ if (LogLevel > 79)
+ sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf);
+
+ if (tTd(40, 1))
+ printf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
+ return;
+}
+
+void
+printctladdr(a, tfp)
+ register ADDRESS *a;
+ FILE *tfp;
+{
+ char *uname;
+ register ADDRESS *q;
+ uid_t uid;
+ gid_t gid;
+ static ADDRESS *lastctladdr = NULL;
+ static uid_t lastuid;
+
+ /* initialization */
+ if (a == NULL || a->q_alias == NULL || tfp == NULL)
+ {
+ if (lastctladdr != NULL && tfp != NULL)
+ fprintf(tfp, "C\n");
+ lastctladdr = NULL;
+ lastuid = 0;
+ return;
+ }
+
+ /* find the active uid */
+ q = getctladdr(a);
+ if (q == NULL)
+ {
+ uname = NULL;
+ uid = 0;
+ gid = 0;
+ }
+ else
+ {
+ uname = q->q_ruser != NULL ? q->q_ruser : q->q_user;
+ uid = q->q_uid;
+ gid = q->q_gid;
+ }
+ a = a->q_alias;
+
+ /* check to see if this is the same as last time */
+ if (lastctladdr != NULL && uid == lastuid &&
+ strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
+ return;
+ lastuid = uid;
+ lastctladdr = a;
+
+ if (uid == 0 || uname == NULL || uname[0] == '\0')
+ fprintf(tfp, "C");
+ else
+ fprintf(tfp, "C%s:%ld:%ld",
+ denlstring(uname, TRUE, FALSE), (long) uid, (long) gid);
+ fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE));
+}
+ /*
+** RUNQUEUE -- run the jobs in the queue.
+**
+** Gets the stuff out of the queue in some presumably logical
+** order and processes them.
+**
+** Parameters:
+** forkflag -- TRUE if the queue scanning should be done in
+** a child process. We double-fork so it is not our
+** child and we don't have to clean up after it.
+** verbose -- if TRUE, print out status information.
+**
+** Returns:
+** TRUE if the queue run successfully began.
+**
+** Side Effects:
+** runs things in the mail queue.
+*/
+
+ENVELOPE QueueEnvelope; /* the queue run envelope */
+extern int get_num_procs_online __P((void));
+
+bool
+runqueue(forkflag, verbose)
+ bool forkflag;
+ bool verbose;
+{
+ register ENVELOPE *e;
+ int njobs;
+ int sequenceno = 0;
+ time_t current_la_time;
+ extern ENVELOPE BlankEnvelope;
+ extern void clrdaemon __P((void));
+ extern void runqueueevent __P((void));
+
+ DoQueueRun = FALSE;
+
+ /*
+ ** If no work will ever be selected, don't even bother reading
+ ** the queue.
+ */
+
+ CurrentLA = getla(); /* get load average */
+ current_la_time = curtime();
+
+ if (shouldqueue(WkRecipFact, current_la_time))
+ {
+ char *msg = "Skipping queue run -- load average too high";
+
+ if (verbose)
+ message("458 %s\n", msg);
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID,
+ "runqueue: %s",
+ msg);
+ if (forkflag && QueueIntvl != 0)
+ (void) setevent(QueueIntvl, runqueueevent, 0);
+ return FALSE;
+ }
+
+ /*
+ ** See if we already have too many children.
+ */
+
+ if (forkflag && QueueIntvl != 0 &&
+ MaxChildren > 0 && CurChildren >= MaxChildren)
+ {
+ (void) setevent(QueueIntvl, runqueueevent, 0);
+ return FALSE;
+ }
+
+ /*
+ ** See if we want to go off and do other useful work.
+ */
+
+ if (forkflag)
+ {
+ pid_t pid;
+ extern SIGFUNC_DECL intsig __P((int));
+ extern SIGFUNC_DECL reapchild __P((int));
+
+ blocksignal(SIGCHLD);
+ (void) setsignal(SIGCHLD, reapchild);
+
+ pid = dofork();
+ if (pid == -1)
+ {
+ const char *msg = "Skipping queue run -- fork() failed";
+ const char *err = errstring(errno);
+
+ if (verbose)
+ message("458 %s: %s\n", msg, err);
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID,
+ "runqueue: %s: %s",
+ msg, err);
+ if (QueueIntvl != 0)
+ (void) setevent(QueueIntvl, runqueueevent, 0);
+ (void) releasesignal(SIGCHLD);
+ return FALSE;
+ }
+ if (pid != 0)
+ {
+ /* parent -- pick up intermediate zombie */
+ (void) blocksignal(SIGALRM);
+ proc_list_add(pid);
+ (void) releasesignal(SIGALRM);
+ releasesignal(SIGCHLD);
+ if (QueueIntvl != 0)
+ (void) setevent(QueueIntvl, runqueueevent, 0);
+ return TRUE;
+ }
+ /* child -- double fork and clean up signals */
+ proc_list_clear();
+ releasesignal(SIGCHLD);
+ (void) setsignal(SIGCHLD, SIG_DFL);
+ (void) setsignal(SIGHUP, intsig);
+ }
+
+ setproctitle("running queue: %s", QueueDir);
+
+ if (LogLevel > 69)
+ sm_syslog(LOG_DEBUG, NOQID,
+ "runqueue %s, pid=%d, forkflag=%d",
+ QueueDir, getpid(), forkflag);
+
+ /*
+ ** Release any resources used by the daemon code.
+ */
+
+# if DAEMON
+ clrdaemon();
+# endif /* DAEMON */
+
+ /* force it to run expensive jobs */
+ NoConnect = FALSE;
+
+ /* drop privileges */
+ if (geteuid() == (uid_t) 0)
+ (void) drop_privileges(FALSE);
+
+ /*
+ ** Create ourselves an envelope
+ */
+
+ CurEnv = &QueueEnvelope;
+ e = newenvelope(&QueueEnvelope, CurEnv);
+ e->e_flags = BlankEnvelope.e_flags;
+
+ /* make sure we have disconnected from parent */
+ if (forkflag)
+ {
+ disconnect(1, e);
+ QuickAbort = FALSE;
+ }
+
+ /*
+ ** Make sure the alias database is open.
+ */
+
+ initmaps(FALSE, e);
+
+ /*
+ ** If we are running part of the queue, always ignore stored
+ ** host status.
+ */
+
+ if (QueueLimitId != NULL || QueueLimitSender != NULL ||
+ QueueLimitRecipient != NULL)
+ {
+ IgnoreHostStatus = TRUE;
+ MinQueueAge = 0;
+ }
+
+ /*
+ ** Start making passes through the queue.
+ ** First, read and sort the entire queue.
+ ** Then, process the work in that order.
+ ** But if you take too long, start over.
+ */
+
+ /* order the existing work requests */
+ njobs = orderq(FALSE);
+
+ /* process them once at a time */
+ while (WorkQ != NULL)
+ {
+ WORK *w = WorkQ;
+
+ WorkQ = WorkQ->w_next;
+ e->e_to = NULL;
+
+ /*
+ ** Ignore jobs that are too expensive for the moment.
+ **
+ ** Get new load average every 30 seconds.
+ */
+
+ if (current_la_time < curtime() - 30)
+ {
+ CurrentLA = getla();
+ current_la_time = curtime();
+ }
+ if (shouldqueue(WkRecipFact, current_la_time))
+ {
+ char *msg = "Aborting queue run: load average too high";
+
+ if (Verbose)
+ message("%s", msg);
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID,
+ "runqueue: %s",
+ msg);
+ break;
+ }
+ sequenceno++;
+ if (shouldqueue(w->w_pri, w->w_ctime))
+ {
+ if (Verbose)
+ message("");
+ if (QueueSortOrder == QS_BYPRIORITY)
+ {
+ if (Verbose)
+ message("Skipping %s (sequence %d of %d) and flushing rest of queue",
+ w->w_name + 2,
+ sequenceno,
+ njobs);
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, NOQID,
+ "runqueue: Flushing queue from %s (pri %ld, LA %d, %d of %d)",
+ w->w_name + 2,
+ w->w_pri,
+ CurrentLA,
+ sequenceno,
+ njobs);
+ break;
+ }
+ else if (Verbose)
+ message("Skipping %s (sequence %d of %d)",
+ w->w_name + 2, sequenceno, njobs);
+ }
+ else
+ {
+ pid_t pid;
+ extern pid_t dowork __P((char *, bool, bool, ENVELOPE *));
+
+ if (Verbose)
+ {
+ message("");
+ message("Running %s (sequence %d of %d)",
+ w->w_name + 2, sequenceno, njobs);
+ }
+ pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e);
+ errno = 0;
+ if (pid != 0)
+ (void) waitfor(pid);
+ }
+ free(w->w_name);
+ if (w->w_host)
+ free(w->w_host);
+ free((char *) w);
+ }
+
+ /* exit without the usual cleanup */
+ e->e_id = NULL;
+ finis();
+ /*NOTREACHED*/
+ return TRUE;
+}
+
+
+/*
+** RUNQUEUEEVENT -- stub for use in setevent
+*/
+
+void
+runqueueevent()
+{
+ DoQueueRun = TRUE;
+}
+ /*
+** ORDERQ -- order the work queue.
+**
+** Parameters:
+** doall -- if set, include everything in the queue (even
+** the jobs that cannot be run because the load
+** average is too high). Otherwise, exclude those
+** jobs.
+**
+** Returns:
+** The number of request in the queue (not necessarily
+** the number of requests in WorkQ however).
+**
+** Side Effects:
+** Sets WorkQ to the queue of available work, in order.
+*/
+
+# define NEED_P 001
+# define NEED_T 002
+# define NEED_R 004
+# define NEED_S 010
+
+static WORK *WorkList = NULL;
+static int WorkListSize = 0;
+
+int
+orderq(doall)
+ bool doall;
+{
+ register struct dirent *d;
+ register WORK *w;
+ register char *p;
+ DIR *f;
+ register int i;
+ int wn = -1;
+ int wc;
+ QUEUE_CHAR *check;
+
+ if (tTd(41, 1))
+ {
+ printf("orderq:\n");
+
+ check = QueueLimitId;
+ while (check != NULL)
+ {
+ printf("\tQueueLimitId = %s\n",
+ check->queue_match);
+ check = check->queue_next;
+ }
+
+ check = QueueLimitSender;
+ while (check != NULL)
+ {
+ printf("\tQueueLimitSender = %s\n",
+ check->queue_match);
+ check = check->queue_next;
+ }
+
+ check = QueueLimitRecipient;
+ while (check != NULL)
+ {
+ printf("\tQueueLimitRecipient = %s\n",
+ check->queue_match);
+ check = check->queue_next;
+ }
+ }
+
+ /* clear out old WorkQ */
+ for (w = WorkQ; w != NULL; )
+ {
+ register WORK *nw = w->w_next;
+
+ WorkQ = nw;
+ free(w->w_name);
+ if (w->w_host)
+ free(w->w_host);
+ free((char *) w);
+ w = nw;
+ }
+
+ /* open the queue directory */
+ f = opendir(".");
+ if (f == NULL)
+ {
+ syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
+ return (0);
+ }
+
+ /*
+ ** Read the work directory.
+ */
+
+ while ((d = readdir(f)) != NULL)
+ {
+ FILE *cf;
+ int qfver = 0;
+ char lbuf[MAXNAME + 1];
+ extern bool strcontainedin __P((char *, char *));
+
+ if (tTd(41, 50))
+ printf("orderq: checking %s\n", d->d_name);
+
+ /* is this an interesting entry? */
+ if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
+ continue;
+
+ if (strlen(d->d_name) > MAXQFNAME)
+ continue;
+
+ check = QueueLimitId;
+ while (check != NULL)
+ {
+ if (strcontainedin(check->queue_match, d->d_name))
+ break;
+ else
+ check = check->queue_next;
+ }
+ if (QueueLimitId != NULL && check == NULL)
+ continue;
+
+#ifdef PICKY_QF_NAME_CHECK
+ /*
+ ** Check queue name for plausibility. This handles
+ ** both old and new type ids.
+ */
+
+ p = d->d_name + 2;
+ if (isupper(p[0]) && isupper(p[2]))
+ p += 3;
+ else if (isupper(p[1]))
+ p += 2;
+ else
+ p = d->d_name;
+ for (i = 0; isdigit(*p); p++)
+ i++;
+ if (i < 5 || *p != '\0')
+ {
+ if (Verbose)
+ printf("orderq: bogus qf name %s\n", d->d_name);
+ if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, NOQID,
+ "orderq: bogus qf name %s",
+ d->d_name);
+ if (strlen(d->d_name) > (SIZE_T) MAXNAME)
+ d->d_name[MAXNAME] = '\0';
+ strcpy(lbuf, d->d_name);
+ lbuf[0] = 'Q';
+ (void) rename(d->d_name, lbuf);
+ continue;
+ }
+#endif
+
+ /* open control file (if not too many files) */
+ if (++wn >= MaxQueueRun && MaxQueueRun > 0)
+ {
+ if (wn == MaxQueueRun && LogLevel > 0)
+ sm_syslog(LOG_ALERT, NOQID,
+ "WorkList for %s maxed out at %d",
+ QueueDir, MaxQueueRun);
+ continue;
+ }
+ if (wn >= WorkListSize)
+ {
+ extern void grow_wlist __P((void));
+
+ grow_wlist();
+ if (wn >= WorkListSize)
+ continue;
+ }
+
+ cf = fopen(d->d_name, "r");
+ if (cf == NULL)
+ {
+ /* this may be some random person sending hir msgs */
+ /* syserr("orderq: cannot open %s", cbuf); */
+ if (tTd(41, 2))
+ printf("orderq: cannot open %s: %s\n",
+ d->d_name, errstring(errno));
+ errno = 0;
+ wn--;
+ continue;
+ }
+ w = &WorkList[wn];
+ w->w_name = newstr(d->d_name);
+ w->w_host = NULL;
+ w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB);
+ w->w_tooyoung = FALSE;
+
+ /* make sure jobs in creation don't clog queue */
+ w->w_pri = 0x7fffffff;
+ w->w_ctime = 0;
+
+ /* extract useful information */
+ i = NEED_P | NEED_T;
+ if (QueueLimitSender != NULL)
+ i |= NEED_S;
+ if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL)
+ i |= NEED_R;
+ while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
+ {
+ int c;
+ time_t age;
+ extern bool strcontainedin __P((char *, char *));
+
+ p = strchr(lbuf, '\n');
+ if (p != NULL)
+ *p = '\0';
+ else
+ {
+ /* flush rest of overly long line */
+ while ((c = getc(cf)) != EOF && c != '\n')
+ continue;
+ }
+
+ switch (lbuf[0])
+ {
+ case 'V':
+ qfver = atoi(&lbuf[1]);
+ break;
+
+ case 'P':
+ w->w_pri = atol(&lbuf[1]);
+ i &= ~NEED_P;
+ break;
+
+ case 'T':
+ w->w_ctime = atol(&lbuf[1]);
+ i &= ~NEED_T;
+ break;
+
+ case 'R':
+ if (w->w_host == NULL &&
+ (p = strrchr(&lbuf[1], '@')) != NULL)
+ w->w_host = newstr(&p[1]);
+ if (QueueLimitRecipient == NULL)
+ {
+ i &= ~NEED_R;
+ break;
+ }
+ if (qfver > 0)
+ {
+ p = strchr(&lbuf[1], ':');
+ if (p == NULL)
+ p = &lbuf[1];
+ }
+ else
+ p = &lbuf[1];
+ check = QueueLimitRecipient;
+ while (check != NULL)
+ {
+ if (strcontainedin(check->queue_match,
+ p))
+ break;
+ else
+ check = check->queue_next;
+ }
+ if (check != NULL)
+ i &= ~NEED_R;
+ break;
+
+ case 'S':
+ check = QueueLimitSender;
+ while (check != NULL)
+ {
+ if (strcontainedin(check->queue_match,
+ &lbuf[1]))
+ break;
+ else
+ check = check->queue_next;
+ }
+ if (check != NULL)
+ i &= ~NEED_S;
+ break;
+
+ case 'K':
+ age = curtime() - (time_t) atol(&lbuf[1]);
+ if (age >= 0 && MinQueueAge > 0 &&
+ age < MinQueueAge)
+ w->w_tooyoung = TRUE;
+ break;
+
+ case 'N':
+ if (atol(&lbuf[1]) == 0)
+ w->w_tooyoung = FALSE;
+ break;
+ }
+ }
+ (void) fclose(cf);
+
+ if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) ||
+ bitset(NEED_R|NEED_S, i))
+ {
+ /* don't even bother sorting this job in */
+ if (tTd(41, 49))
+ printf("skipping %s (%x)\n", w->w_name, i);
+ free(w->w_name);
+ if (w->w_host)
+ free(w->w_host);
+ wn--;
+ }
+ }
+ (void) closedir(f);
+ wn++;
+
+ wc = min(wn, WorkListSize);
+ if (wc > MaxQueueRun && MaxQueueRun > 0)
+ wc = MaxQueueRun;
+
+ if (QueueSortOrder == QS_BYHOST)
+ {
+ extern int workcmpf1();
+ extern int workcmpf2();
+
+ /*
+ ** Sort the work directory for the first time,
+ ** based on host name, lock status, and priority.
+ */
+
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1);
+
+ /*
+ ** If one message to host is locked, "lock" all messages
+ ** to that host.
+ */
+
+ i = 0;
+ while (i < wc)
+ {
+ if (!WorkList[i].w_lock)
+ {
+ i++;
+ continue;
+ }
+ w = &WorkList[i];
+ while (++i < wc)
+ {
+ extern int sm_strcasecmp __P((char *, char *));
+
+ if (WorkList[i].w_host == NULL &&
+ w->w_host == NULL)
+ WorkList[i].w_lock = TRUE;
+ else if (WorkList[i].w_host != NULL &&
+ w->w_host != NULL &&
+ sm_strcasecmp(WorkList[i].w_host, w->w_host) == 0)
+ WorkList[i].w_lock = TRUE;
+ else
+ break;
+ }
+ }
+
+ /*
+ ** Sort the work directory for the second time,
+ ** based on lock status, host name, and priority.
+ */
+
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2);
+ }
+ else if (QueueSortOrder == QS_BYTIME)
+ {
+ extern int workcmpf3();
+
+ /*
+ ** Simple sort based on submission time only.
+ */
+
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3);
+ }
+ else
+ {
+ extern int workcmpf0();
+
+ /*
+ ** Simple sort based on queue priority only.
+ */
+
+ qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0);
+ }
+
+ /*
+ ** Convert the work list into canonical form.
+ ** Should be turning it into a list of envelopes here perhaps.
+ */
+
+ WorkQ = NULL;
+ for (i = wc; --i >= 0; )
+ {
+ w = (WORK *) xalloc(sizeof *w);
+ w->w_name = WorkList[i].w_name;
+ w->w_host = WorkList[i].w_host;
+ w->w_lock = WorkList[i].w_lock;
+ w->w_tooyoung = WorkList[i].w_tooyoung;
+ w->w_pri = WorkList[i].w_pri;
+ w->w_ctime = WorkList[i].w_ctime;
+ w->w_next = WorkQ;
+ WorkQ = w;
+ }
+ if (WorkList != NULL)
+ free(WorkList);
+ WorkList = NULL;
+ WorkListSize = 0;
+
+ if (tTd(40, 1))
+ {
+ for (w = WorkQ; w != NULL; w = w->w_next)
+ printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
+ }
+
+ return (wn);
+}
+ /*
+** GROW_WLIST -- make the work list larger
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Adds another QUEUESEGSIZE entries to WorkList if possible.
+** It can fail if there isn't enough memory, so WorkListSize
+** should be checked again upon return.
+*/
+
+void
+grow_wlist()
+{
+ if (tTd(41, 1))
+ printf("grow_wlist: WorkListSize=%d\n", WorkListSize);
+ if (WorkList == NULL)
+ {
+ WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1));
+ WorkListSize = QUEUESEGSIZE;
+ }
+ else
+ {
+ int newsize = WorkListSize + QUEUESEGSIZE;
+ WORK *newlist = (WORK *) realloc((char *)WorkList,
+ (unsigned)sizeof(WORK) * (newsize + 1));
+
+ if (newlist != NULL)
+ {
+ WorkListSize = newsize;
+ WorkList = newlist;
+ if (LogLevel > 1)
+ {
+ sm_syslog(LOG_NOTICE, NOQID,
+ "grew WorkList for %s to %d",
+ QueueDir, WorkListSize);
+ }
+ }
+ else if (LogLevel > 0)
+ {
+ sm_syslog(LOG_ALERT, NOQID,
+ "FAILED to grow WorkList for %s to %d",
+ QueueDir, newsize);
+ }
+ }
+ if (tTd(41, 1))
+ printf("grow_wlist: WorkListSize now %d\n", WorkListSize);
+}
+ /*
+** WORKCMPF0 -- simple priority-only compare function.
+**
+** Parameters:
+** a -- the first argument.
+** b -- the second argument.
+**
+** Returns:
+** -1 if a < b
+** 0 if a == b
+** +1 if a > b
+**
+** Side Effects:
+** none.
+*/
+
+int
+workcmpf0(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ long pa = a->w_pri;
+ long pb = b->w_pri;
+
+ if (pa == pb)
+ return 0;
+ else if (pa > pb)
+ return 1;
+ else
+ return -1;
+}
+ /*
+** WORKCMPF1 -- first compare function for ordering work based on host name.
+**
+** Sorts on host name, lock status, and priority in that order.
+**
+** Parameters:
+** a -- the first argument.
+** b -- the second argument.
+**
+** Returns:
+** <0 if a < b
+** 0 if a == b
+** >0 if a > b
+**
+** Side Effects:
+** none.
+*/
+
+int
+workcmpf1(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ int i;
+ extern int sm_strcasecmp __P((char *, char *));
+
+ /* host name */
+ if (a->w_host != NULL && b->w_host == NULL)
+ return 1;
+ else if (a->w_host == NULL && b->w_host != NULL)
+ return -1;
+ if (a->w_host != NULL && b->w_host != NULL &&
+ (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
+ return i;
+
+ /* lock status */
+ if (a->w_lock != b->w_lock)
+ return b->w_lock - a->w_lock;
+
+ /* job priority */
+ return a->w_pri - b->w_pri;
+}
+ /*
+** WORKCMPF2 -- second compare function for ordering work based on host name.
+**
+** Sorts on lock status, host name, and priority in that order.
+**
+** Parameters:
+** a -- the first argument.
+** b -- the second argument.
+**
+** Returns:
+** <0 if a < b
+** 0 if a == b
+** >0 if a > b
+**
+** Side Effects:
+** none.
+*/
+
+int
+workcmpf2(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ int i;
+ extern int sm_strcasecmp __P((char *, char *));
+
+ /* lock status */
+ if (a->w_lock != b->w_lock)
+ return a->w_lock - b->w_lock;
+
+ /* host name */
+ if (a->w_host != NULL && b->w_host == NULL)
+ return 1;
+ else if (a->w_host == NULL && b->w_host != NULL)
+ return -1;
+ if (a->w_host != NULL && b->w_host != NULL &&
+ (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
+ return i;
+
+ /* job priority */
+ return a->w_pri - b->w_pri;
+}
+ /*
+** WORKCMPF3 -- simple submission-time-only compare function.
+**
+** Parameters:
+** a -- the first argument.
+** b -- the second argument.
+**
+** Returns:
+** -1 if a < b
+** 0 if a == b
+** +1 if a > b
+**
+** Side Effects:
+** none.
+*/
+
+int
+workcmpf3(a, b)
+ register WORK *a;
+ register WORK *b;
+{
+ if (a->w_ctime > b->w_ctime)
+ return 1;
+ else if (a->w_ctime < b->w_ctime)
+ return -1;
+ else
+ return 0;
+}
+ /*
+** DOWORK -- do a work request.
+**
+** Parameters:
+** id -- the ID of the job to run.
+** forkflag -- if set, run this in background.
+** requeueflag -- if set, reinstantiate the queue quickly.
+** This is used when expanding aliases in the queue.
+** If forkflag is also set, it doesn't wait for the
+** child.
+** e - the envelope in which to run it.
+**
+** Returns:
+** process id of process that is running the queue job.
+**
+** Side Effects:
+** The work request is satisfied if possible.
+*/
+
+pid_t
+dowork(id, forkflag, requeueflag, e)
+ char *id;
+ bool forkflag;
+ bool requeueflag;
+ register ENVELOPE *e;
+{
+ register pid_t pid;
+ extern bool readqf __P((ENVELOPE *));
+
+ if (tTd(40, 1))
+ printf("dowork(%s)\n", id);
+
+ /*
+ ** Fork for work.
+ */
+
+ if (forkflag)
+ {
+ pid = fork();
+ if (pid < 0)
+ {
+ syserr("dowork: cannot fork");
+ return 0;
+ }
+ else if (pid > 0)
+ {
+ /* parent -- clean out connection cache */
+ mci_flush(FALSE, NULL);
+ }
+ else
+ {
+ /* child -- error messages to the transcript */
+ QuickAbort = OnlyOneError = FALSE;
+ }
+ }
+ else
+ {
+ pid = 0;
+ }
+
+ if (pid == 0)
+ {
+ /*
+ ** CHILD
+ ** Lock the control file to avoid duplicate deliveries.
+ ** Then run the file as though we had just read it.
+ ** We save an idea of the temporary name so we
+ ** can recover on interrupt.
+ */
+
+ /* set basic modes, etc. */
+ (void) alarm(0);
+ clearenvelope(e, FALSE);
+ e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
+ e->e_sendmode = SM_DELIVER;
+ e->e_errormode = EM_MAIL;
+ e->e_id = id;
+ GrabTo = UseErrorsTo = FALSE;
+ ExitStat = EX_OK;
+ if (forkflag)
+ {
+ disconnect(1, e);
+ OpMode = MD_DELIVER;
+ }
+ setproctitle("%s: from queue", id);
+ if (LogLevel > 76)
+ sm_syslog(LOG_DEBUG, e->e_id,
+ "dowork, pid=%d",
+ getpid());
+
+ /* don't use the headers from sendmail.cf... */
+ e->e_header = NULL;
+
+ /* read the queue control file -- return if locked */
+ if (!readqf(e))
+ {
+ if (tTd(40, 4) && e->e_id != NULL)
+ printf("readqf(%s) failed\n", e->e_id);
+ e->e_id = NULL;
+ if (forkflag)
+ exit(EX_OK);
+ else
+ return 0;
+ }
+
+ e->e_flags |= EF_INQUEUE;
+ eatheader(e, requeueflag);
+
+ if (requeueflag)
+ queueup(e, FALSE);
+
+ /* do the delivery */
+ sendall(e, SM_DELIVER);
+
+ /* finish up and exit */
+ if (forkflag)
+ finis();
+ else
+ dropenvelope(e, TRUE);
+ }
+ e->e_id = NULL;
+ return pid;
+}
+ /*
+** READQF -- read queue file and set up environment.
+**
+** Parameters:
+** e -- the envelope of the job to run.
+**
+** Returns:
+** TRUE if it successfully read the queue file.
+** FALSE otherwise.
+**
+** Side Effects:
+** The queue file is returned locked.
+*/
+
+bool
+readqf(e)
+ register ENVELOPE *e;
+{
+ register FILE *qfp;
+ ADDRESS *ctladdr;
+ struct stat st;
+ char *bp;
+ int qfver = 0;
+ long hdrsize = 0;
+ register char *p;
+ char *orcpt = NULL;
+ bool nomore = FALSE;
+ char qf[MAXQFNAME];
+ char buf[MAXLINE];
+ extern ADDRESS *setctluser __P((char *, int));
+
+ /*
+ ** Read and process the file.
+ */
+
+ strcpy(qf, queuename(e, 'q'));
+ qfp = fopen(qf, "r+");
+ if (qfp == NULL)
+ {
+ if (tTd(40, 8))
+ printf("readqf(%s): fopen failure (%s)\n",
+ qf, errstring(errno));
+ if (errno != ENOENT)
+ syserr("readqf: no control file %s", qf);
+ return FALSE;
+ }
+
+ if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
+ {
+ /* being processed by another queuer */
+ if (Verbose || tTd(40, 8))
+ printf("%s: locked\n", e->e_id);
+ if (LogLevel > 19)
+ sm_syslog(LOG_DEBUG, e->e_id, "locked");
+ (void) fclose(qfp);
+ return FALSE;
+ }
+
+ /*
+ ** Check the queue file for plausibility to avoid attacks.
+ */
+
+ if (fstat(fileno(qfp), &st) < 0)
+ {
+ /* must have been being processed by someone else */
+ if (tTd(40, 8))
+ printf("readqf(%s): fstat failure (%s)\n",
+ qf, errstring(errno));
+ fclose(qfp);
+ return FALSE;
+ }
+
+ if ((st.st_uid != geteuid() && geteuid() != RealUid) ||
+ bitset(S_IWOTH|S_IWGRP, st.st_mode))
+ {
+ if (LogLevel > 0)
+ {
+ sm_syslog(LOG_ALERT, e->e_id,
+ "bogus queue file, uid=%d, mode=%o",
+ st.st_uid, st.st_mode);
+ }
+ if (tTd(40, 8))
+ printf("readqf(%s): bogus file\n", qf);
+ loseqfile(e, "bogus file uid in mqueue");
+ fclose(qfp);
+ return FALSE;
+ }
+
+ if (st.st_size == 0)
+ {
+ /* must be a bogus file -- if also old, just remove it */
+ if (st.st_ctime + 10 * 60 < curtime())
+ {
+ qf[0] = 'd';
+ (void) unlink(qf);
+ qf[0] = 'q';
+ (void) unlink(qf);
+ }
+ fclose(qfp);
+ return FALSE;
+ }
+
+ if (st.st_nlink == 0)
+ {
+ /*
+ ** Race condition -- we got a file just as it was being
+ ** unlinked. Just assume it is zero length.
+ */
+
+ fclose(qfp);
+ return FALSE;
+ }
+
+ /* good file -- save this lock */
+ e->e_lockfp = qfp;
+
+ /* do basic system initialization */
+ initsys(e);
+ define('i', e->e_id, e);
+
+ LineNumber = 0;
+ e->e_flags |= EF_GLOBALERRS;
+ OpMode = MD_DELIVER;
+ ctladdr = NULL;
+ e->e_dfino = -1;
+ e->e_msgsize = -1;
+ while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
+ {
+ register char *p;
+ u_long qflags;
+ ADDRESS *q;
+ int mid;
+ auto char *ep;
+
+ if (tTd(40, 4))
+ printf("+++++ %s\n", bp);
+ if (nomore)
+ {
+ /* hack attack */
+ syserr("SECURITY ALERT: extra data in qf: %s", bp);
+ fclose(qfp);
+ loseqfile(e, "bogus queue line");
+ return FALSE;
+ }
+ switch (bp[0])
+ {
+ case 'V': /* queue file version number */
+ qfver = atoi(&bp[1]);
+ if (qfver <= QF_VERSION)
+ break;
+ syserr("Version number in qf (%d) greater than max (%d)",
+ qfver, QF_VERSION);
+ fclose(qfp);
+ loseqfile(e, "unsupported qf file version");
+ return FALSE;
+
+ case 'C': /* specify controlling user */
+ ctladdr = setctluser(&bp[1], qfver);
+ break;
+
+ case 'Q': /* original recipient */
+ orcpt = newstr(&bp[1]);
+ break;
+
+ case 'R': /* specify recipient */
+ p = bp;
+ qflags = 0;
+ if (qfver >= 1)
+ {
+ /* get flag bits */
+ while (*++p != '\0' && *p != ':')
+ {
+ switch (*p)
+ {
+ case 'N':
+ qflags |= QHASNOTIFY;
+ break;
+
+ case 'S':
+ qflags |= QPINGONSUCCESS;
+ break;
+
+ case 'F':
+ qflags |= QPINGONFAILURE;
+ break;
+
+ case 'D':
+ qflags |= QPINGONDELAY;
+ break;
+
+ case 'P':
+ qflags |= QPRIMARY;
+ break;
+ }
+ }
+ }
+ else
+ qflags |= QPRIMARY;
+ q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e);
+ if (q != NULL)
+ {
+ q->q_alias = ctladdr;
+ if (qfver >= 1)
+ q->q_flags &= ~Q_PINGFLAGS;
+ q->q_flags |= qflags;
+ q->q_orcpt = orcpt;
+ (void) recipient(q, &e->e_sendqueue, 0, e);
+ }
+ orcpt = NULL;
+ break;
+
+ case 'E': /* specify error recipient */
+ /* no longer used */
+ break;
+
+ case 'H': /* header */
+ (void) chompheader(&bp[1], FALSE, NULL, e);
+ hdrsize += strlen(&bp[1]);
+ break;
+
+ case 'L': /* Solaris Content-Length: */
+ case 'M': /* message */
+ /* ignore this; we want a new message next time */
+ break;
+
+ case 'S': /* sender */
+ setsender(newstr(&bp[1]), e, NULL, '\0', TRUE);
+ break;
+
+ case 'B': /* body type */
+ e->e_bodytype = newstr(&bp[1]);
+ break;
+
+#if _FFR_SAVE_CHARSET
+ case 'X': /* character set */
+ e->e_charset = newstr(&bp[1]);
+ break;
+#endif
+
+ case 'D': /* data file name */
+ /* obsolete -- ignore */
+ break;
+
+ case 'T': /* init time */
+ e->e_ctime = atol(&bp[1]);
+ break;
+
+ case 'I': /* data file's inode number */
+ /* regenerated below */
+ break;
+
+ case 'K': /* time of last deliver attempt */
+ e->e_dtime = atol(&buf[1]);
+ break;
+
+ case 'N': /* number of delivery attempts */
+ e->e_ntries = atoi(&buf[1]);
+
+ /* if this has been tried recently, let it be */
+ if (e->e_ntries > 0 &&
+ MinQueueAge > 0 && e->e_dtime <= curtime() &&
+ curtime() < e->e_dtime + MinQueueAge)
+ {
+ char *howlong = pintvl(curtime() - e->e_dtime, TRUE);
+ extern void unlockqueue __P((ENVELOPE *));
+
+ if (Verbose || tTd(40, 8))
+ printf("%s: too young (%s)\n",
+ e->e_id, howlong);
+ if (LogLevel > 19)
+ sm_syslog(LOG_DEBUG, e->e_id,
+ "too young (%s)",
+ howlong);
+ e->e_id = NULL;
+ unlockqueue(e);
+ return FALSE;
+ }
+ break;
+
+ case 'P': /* message priority */
+ e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
+ break;
+
+ case 'F': /* flag bits */
+ if (strncmp(bp, "From ", 5) == 0)
+ {
+ /* we are being spoofed! */
+ syserr("SECURITY ALERT: bogus qf line %s", bp);
+ fclose(qfp);
+ loseqfile(e, "bogus queue line");
+ return FALSE;
+ }
+ for (p = &bp[1]; *p != '\0'; p++)
+ {
+ switch (*p)
+ {
+ case 'w': /* warning sent */
+ e->e_flags |= EF_WARNING;
+ break;
+
+ case 'r': /* response */
+ e->e_flags |= EF_RESPONSE;
+ break;
+
+ case '8': /* has 8 bit data */
+ e->e_flags |= EF_HAS8BIT;
+ break;
+
+ case 'b': /* delete Bcc: header */
+ e->e_flags |= EF_DELETE_BCC;
+ break;
+
+ case 'd': /* envelope has DSN RET= */
+ e->e_flags |= EF_RET_PARAM;
+ break;
+
+ case 'n': /* don't return body */
+ e->e_flags |= EF_NO_BODY_RETN;
+ break;
+ }
+ }
+ break;
+
+ case 'Z': /* original envelope id from ESMTP */
+ e->e_envid = newstr(&bp[1]);
+ break;
+
+ case '$': /* define macro */
+ mid = macid(&bp[1], &ep);
+ define(mid, newstr(ep), e);
+ break;
+
+ case '.': /* terminate file */
+ nomore = TRUE;
+ break;
+
+ default:
+ syserr("readqf: %s: line %d: bad line \"%s\"",
+ qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
+ fclose(qfp);
+ loseqfile(e, "unrecognized line");
+ return FALSE;
+ }
+
+ if (bp != buf)
+ free(bp);
+ }
+
+ /*
+ ** If we haven't read any lines, this queue file is empty.
+ ** Arrange to remove it without referencing any null pointers.
+ */
+
+ if (LineNumber == 0)
+ {
+ errno = 0;
+ e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
+ return TRUE;
+ }
+
+ /*
+ ** Arrange to read the data file.
+ */
+
+ p = queuename(e, 'd');
+ e->e_dfp = fopen(p, "r");
+ if (e->e_dfp == NULL)
+ {
+ syserr("readqf: cannot open %s", p);
+ }
+ else
+ {
+ e->e_flags |= EF_HAS_DF;
+ if (fstat(fileno(e->e_dfp), &st) >= 0)
+ {
+ e->e_msgsize = st.st_size + hdrsize;
+ e->e_dfdev = st.st_dev;
+ e->e_dfino = st.st_ino;
+ }
+ }
+
+ return TRUE;
+}
+ /*
+** PRINTQUEUE -- print out a representation of the mail queue
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Prints a listing of the mail queue on the standard output.
+*/
+
+void
+printqueue()
+{
+ register WORK *w;
+ FILE *f;
+ int nrequests;
+ char buf[MAXLINE];
+
+ /*
+ ** Check for permission to print the queue
+ */
+
+ if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
+ {
+ struct stat st;
+# ifdef NGROUPS_MAX
+ int n;
+ extern GIDSET_T InitialGidSet[NGROUPS_MAX];
+# endif
+
+ if (stat(QueueDir, &st) < 0)
+ {
+ syserr("Cannot stat %s", QueueDir);
+ return;
+ }
+# ifdef NGROUPS_MAX
+ n = NGROUPS_MAX;
+ while (--n >= 0)
+ {
+ if (InitialGidSet[n] == st.st_gid)
+ break;
+ }
+ if (n < 0 && RealGid != st.st_gid)
+# else
+ if (RealGid != st.st_gid)
+# endif
+ {
+ usrerr("510 You are not permitted to see the queue");
+ setstat(EX_NOPERM);
+ return;
+ }
+ }
+
+ /*
+ ** Read and order the queue.
+ */
+
+ nrequests = orderq(TRUE);
+
+ /*
+ ** Print the work list that we have read.
+ */
+
+ /* first see if there is anything */
+ if (nrequests <= 0)
+ {
+ printf("Mail queue is empty\n");
+ return;
+ }
+
+ CurrentLA = getla(); /* get load average */
+
+ printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
+ if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
+ printf(", only %d printed", MaxQueueRun);
+ if (Verbose)
+ printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
+ else
+ printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
+ for (w = WorkQ; w != NULL; w = w->w_next)
+ {
+ struct stat st;
+ auto time_t submittime = 0;
+ long dfsize;
+ int flags = 0;
+ int qfver;
+ char statmsg[MAXLINE];
+ char bodytype[MAXNAME + 1];
+
+ printf("%8s", w->w_name + 2);
+ f = fopen(w->w_name, "r");
+ if (f == NULL)
+ {
+ printf(" (job completed)\n");
+ errno = 0;
+ continue;
+ }
+ w->w_name[0] = 'd';
+ if (stat(w->w_name, &st) >= 0)
+ dfsize = st.st_size;
+ else
+ dfsize = -1;
+ if (w->w_lock)
+ printf("*");
+ else if (w->w_tooyoung)
+ printf("-");
+ else if (shouldqueue(w->w_pri, w->w_ctime))
+ printf("X");
+ else
+ printf(" ");
+ errno = 0;
+
+ statmsg[0] = bodytype[0] = '\0';
+ qfver = 0;
+ while (fgets(buf, sizeof buf, f) != NULL)
+ {
+ register int i;
+ register char *p;
+
+ fixcrlf(buf, TRUE);
+ switch (buf[0])
+ {
+ case 'V': /* queue file version */
+ qfver = atoi(&buf[1]);
+ break;
+
+ case 'M': /* error message */
+ if ((i = strlen(&buf[1])) >= sizeof statmsg)
+ i = sizeof statmsg - 1;
+ bcopy(&buf[1], statmsg, i);
+ statmsg[i] = '\0';
+ break;
+
+ case 'B': /* body type */
+ if ((i = strlen(&buf[1])) >= sizeof bodytype)
+ i = sizeof bodytype - 1;
+ bcopy(&buf[1], bodytype, i);
+ bodytype[i] = '\0';
+ break;
+
+ case 'S': /* sender name */
+ if (Verbose)
+ printf("%8ld %10ld%c%.12s %.78s",
+ dfsize,
+ w->w_pri,
+ bitset(EF_WARNING, flags) ? '+' : ' ',
+ ctime(&submittime) + 4,
+ &buf[1]);
+ else
+ printf("%8ld %.16s %.45s", dfsize,
+ ctime(&submittime), &buf[1]);
+ if (statmsg[0] != '\0' || bodytype[0] != '\0')
+ {
+ printf("\n %10.10s", bodytype);
+ if (statmsg[0] != '\0')
+ printf(" (%.*s)",
+ Verbose ? 100 : 60,
+ statmsg);
+ }
+ break;
+
+ case 'C': /* controlling user */
+ if (Verbose)
+ printf("\n\t\t\t\t (---%.74s---)",
+ &buf[1]);
+ break;
+
+ case 'R': /* recipient name */
+ p = &buf[1];
+ if (qfver >= 1)
+ {
+ p = strchr(p, ':');
+ if (p == NULL)
+ break;
+ p++;
+ }
+ if (Verbose)
+ printf("\n\t\t\t\t\t %.78s", p);
+ else
+ printf("\n\t\t\t\t %.45s", p);
+ break;
+
+ case 'T': /* creation time */
+ submittime = atol(&buf[1]);
+ break;
+
+ case 'F': /* flag bits */
+ for (p = &buf[1]; *p != '\0'; p++)
+ {
+ switch (*p)
+ {
+ case 'w':
+ flags |= EF_WARNING;
+ break;
+ }
+ }
+ }
+ }
+ if (submittime == (time_t) 0)
+ printf(" (no control file)");
+ printf("\n");
+ (void) fclose(f);
+ }
+}
+
+# endif /* QUEUE */
+ /*
+** QUEUENAME -- build a file name in the queue directory for this envelope.
+**
+** Assigns an id code if one does not already exist.
+** This code is very careful to avoid trashing existing files
+** under any circumstances.
+**
+** Parameters:
+** e -- envelope to build it in/from.
+** type -- the file type, used as the first character
+** of the file name.
+**
+** Returns:
+** a pointer to the new file name (in a static buffer).
+**
+** Side Effects:
+** If no id code is already assigned, queuename will
+** assign an id code, create a qf file, and leave a
+** locked, open-for-write file pointer in the envelope.
+*/
+
+#ifndef ENOLCK
+# define ENOLCK -1
+#endif
+#ifndef ENOSPC
+# define ENOSPC -1
+#endif
+
+char *
+queuename(e, type)
+ register ENVELOPE *e;
+ int type;
+{
+ static pid_t pid = -1;
+ static char c0;
+ static char c1;
+ static char c2;
+ time_t now;
+ struct tm *tm;
+ static char buf[MAXNAME + 1];
+
+ if (e->e_id == NULL)
+ {
+ char qf[MAXQFNAME];
+
+ /* find a unique id */
+ if (pid != getpid())
+ {
+ /* new process -- start back at "AA" */
+ pid = getpid();
+ now = curtime();
+ tm = localtime(&now);
+ c0 = 'A' + tm->tm_hour;
+ c1 = 'A';
+ c2 = 'A' - 1;
+ }
+ (void) snprintf(qf, sizeof qf, "qf%cAA%05d", c0, pid);
+
+ while (c1 < '~' || c2 < 'Z')
+ {
+ int i;
+ int attempts = 0;
+
+ if (c2 >= 'Z')
+ {
+ c1++;
+ c2 = 'A' - 1;
+ }
+ qf[3] = c1;
+ qf[4] = ++c2;
+ if (tTd(7, 20))
+ printf("queuename: trying \"%s\"\n", qf);
+
+ i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode);
+ if (i < 0)
+ {
+ if (errno == EEXIST)
+ continue;
+ syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
+ qf, QueueDir, geteuid());
+ exit(EX_UNAVAILABLE);
+ }
+ do
+ {
+ if (attempts > 0)
+ sleep(attempts);
+ e->e_lockfp = 0;
+ if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB))
+ {
+ e->e_lockfp = fdopen(i, "w");
+ break;
+ }
+ } while ((errno == ENOLCK || errno == ENOSPC) &&
+ attempts++ < 4);
+
+ /* Successful lock */
+ if (e->e_lockfp != 0)
+ break;
+
+#if !HASFLOCK
+ if (errno != EAGAIN && errno != EACCES)
+#else
+ if (errno != EWOULDBLOCK)
+#endif
+ {
+ syserr("queuename: Cannot lock \"%s\" in \"%s\" (euid=%d)",
+ qf, QueueDir, geteuid());
+ exit(EX_OSERR);
+ }
+
+ /* a reader got the file; abandon it and try again */
+ (void) close(i);
+ }
+ if (c1 >= '~' && c2 >= 'Z')
+ {
+ syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
+ qf, QueueDir, geteuid());
+ exit(EX_OSERR);
+ }
+ e->e_id = newstr(&qf[2]);
+ define('i', e->e_id, e);
+ if (tTd(7, 1))
+ printf("queuename: assigned id %s, env=%lx\n",
+ e->e_id, (u_long) e);
+ if (tTd(7, 9))
+ {
+ printf(" lockfd=");
+ dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
+ }
+ if (LogLevel > 93)
+ sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
+ }
+
+ if (type == '\0')
+ return (NULL);
+ (void) snprintf(buf, sizeof buf, "%cf%s", type, e->e_id);
+ if (tTd(7, 2))
+ printf("queuename: %s\n", buf);
+ return (buf);
+}
+ /*
+** UNLOCKQUEUE -- unlock the queue entry for a specified envelope
+**
+** Parameters:
+** e -- the envelope to unlock.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** unlocks the queue for `e'.
+*/
+
+void
+unlockqueue(e)
+ ENVELOPE *e;
+{
+ if (tTd(51, 4))
+ printf("unlockqueue(%s)\n",
+ e->e_id == NULL ? "NOQUEUE" : e->e_id);
+
+ /* if there is a lock file in the envelope, close it */
+ if (e->e_lockfp != NULL)
+ xfclose(e->e_lockfp, "unlockqueue", e->e_id);
+ e->e_lockfp = NULL;
+
+ /* don't create a queue id if we don't already have one */
+ if (e->e_id == NULL)
+ return;
+
+ /* remove the transcript */
+ if (LogLevel > 87)
+ sm_syslog(LOG_DEBUG, e->e_id, "unlock");
+ if (!tTd(51, 104))
+ xunlink(queuename(e, 'x'));
+
+}
+ /*
+** SETCTLUSER -- create a controlling address
+**
+** Create a fake "address" given only a local login name; this is
+** used as a "controlling user" for future recipient addresses.
+**
+** Parameters:
+** user -- the user name of the controlling user.
+** qfver -- the version stamp of this qf file.
+**
+** Returns:
+** An address descriptor for the controlling user.
+**
+** Side Effects:
+** none.
+*/
+
+ADDRESS *
+setctluser(user, qfver)
+ char *user;
+ int qfver;
+{
+ register ADDRESS *a;
+ struct passwd *pw;
+ char *p;
+
+ /*
+ ** See if this clears our concept of controlling user.
+ */
+
+ if (user == NULL || *user == '\0')
+ return NULL;
+
+ /*
+ ** Set up addr fields for controlling user.
+ */
+
+ a = (ADDRESS *) xalloc(sizeof *a);
+ bzero((char *) a, sizeof *a);
+
+ if (*user == '\0')
+ {
+ p = NULL;
+ a->q_user = newstr(DefUser);
+ }
+ else if (*user == ':')
+ {
+ p = &user[1];
+ a->q_user = newstr(p);
+ }
+ else
+ {
+ p = strtok(user, ":");
+ a->q_user = newstr(user);
+ if (qfver >= 2)
+ {
+ if ((p = strtok(NULL, ":")) != NULL)
+ a->q_uid = atoi(p);
+ if ((p = strtok(NULL, ":")) != NULL)
+ a->q_gid = atoi(p);
+ if ((p = strtok(NULL, ":")) != NULL)
+ a->q_flags |= QGOODUID;
+ }
+ else if ((pw = sm_getpwnam(user)) != NULL)
+ {
+ if (strcmp(pw->pw_dir, "/") == 0)
+ a->q_home = "";
+ else
+ a->q_home = newstr(pw->pw_dir);
+ a->q_uid = pw->pw_uid;
+ a->q_gid = pw->pw_gid;
+ a->q_flags |= QGOODUID;
+ }
+ }
+
+ a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */
+ a->q_mailer = LocalMailer;
+ if (p == NULL)
+ a->q_paddr = a->q_user;
+ else
+ a->q_paddr = newstr(p);
+ return a;
+}
+ /*
+** LOSEQFILE -- save the qf as Qf and try to let someone know
+**
+** Parameters:
+** e -- the envelope (e->e_id will be used).
+** why -- reported to whomever can hear.
+**
+** Returns:
+** none.
+*/
+
+void
+loseqfile(e, why)
+ register ENVELOPE *e;
+ char *why;
+{
+ char *p;
+ char buf[MAXQFNAME + 1];
+
+ if (e == NULL || e->e_id == NULL)
+ return;
+ p = queuename(e, 'q');
+ if (strlen(p) > MAXQFNAME)
+ {
+ syserr("loseqfile: queuename (%s) too long", p);
+ return;
+ }
+ strcpy(buf, p);
+ p = queuename(e, 'Q');
+ if (rename(buf, p) < 0)
+ syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid());
+ else if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, e->e_id,
+ "Losing %s: %s", buf, why);
+}
diff --git a/contrib/sendmail/src/readcf.c b/contrib/sendmail/src/readcf.c
new file mode 100644
index 0000000..db71937
--- /dev/null
+++ b/contrib/sendmail/src/readcf.c
@@ -0,0 +1,2868 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)readcf.c 8.230 (Berkeley) 6/5/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include <grp.h>
+#if NAMED_BIND
+# include <resolv.h>
+#endif
+
+/*
+** READCF -- read configuration file.
+**
+** This routine reads the configuration file and builds the internal
+** form.
+**
+** The file is formatted as a sequence of lines, each taken
+** atomically. The first character of each line describes how
+** the line is to be interpreted. The lines are:
+** Dxval Define macro x to have value val.
+** Cxword Put word into class x.
+** Fxfile [fmt] Read file for lines to put into
+** class x. Use scanf string 'fmt'
+** or "%s" if not present. Fmt should
+** only produce one string-valued result.
+** Hname: value Define header with field-name 'name'
+** and value as specified; this will be
+** macro expanded immediately before
+** use.
+** Sn Use rewriting set n.
+** Rlhs rhs Rewrite addresses that match lhs to
+** be rhs.
+** Mn arg=val... Define mailer. n is the internal name.
+** Args specify mailer parameters.
+** Oxvalue Set option x to value.
+** Pname=value Set precedence name to value.
+** Vversioncode[/vendorcode]
+** Version level/vendor name of
+** configuration syntax.
+** Kmapname mapclass arguments....
+** Define keyed lookup of a given class.
+** Arguments are class dependent.
+** Eenvar=value Set the environment value to the given value.
+**
+** Parameters:
+** cfname -- configuration file name.
+** safe -- TRUE if this is the system config file;
+** FALSE otherwise.
+** e -- the main envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Builds several internal tables.
+*/
+
+void
+readcf(cfname, safe, e)
+ char *cfname;
+ bool safe;
+ register ENVELOPE *e;
+{
+ FILE *cf;
+ int ruleset = 0;
+ char *q;
+ struct rewrite *rwp = NULL;
+ char *bp;
+ auto char *ep;
+ int nfuzzy;
+ char *file;
+ bool optional;
+ int mid;
+ register char *p;
+ int sff = SFF_OPENASROOT;
+ struct stat statb;
+ char buf[MAXLINE];
+ char exbuf[MAXLINE];
+ char pvpbuf[MAXLINE + MAXATOM];
+ static char *null_list[1] = { NULL };
+ extern char **copyplist __P((char **, bool));
+ extern char *munchstring __P((char *, char **, int));
+ extern void fileclass __P((int, char *, char *, bool, bool));
+ extern void toomany __P((int, int));
+ extern void translate_dollars __P((char *));
+ extern void inithostmaps __P((void));
+
+ FileName = cfname;
+ LineNumber = 0;
+
+ if (DontLockReadFiles)
+ sff |= SFF_NOLOCK;
+ cf = safefopen(cfname, O_RDONLY, 0444, sff);
+ if (cf == NULL)
+ {
+ syserr("cannot open");
+ exit(EX_OSFILE);
+ }
+
+ if (fstat(fileno(cf), &statb) < 0)
+ {
+ syserr("cannot fstat");
+ exit(EX_OSFILE);
+ }
+
+ if (!S_ISREG(statb.st_mode))
+ {
+ syserr("not a plain file");
+ exit(EX_OSFILE);
+ }
+
+ if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
+ {
+ if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
+ fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
+ FileName);
+ if (LogLevel > 0)
+ sm_syslog(LOG_CRIT, NOQID,
+ "%s: WARNING: dangerous write permissions",
+ FileName);
+ }
+
+#ifdef XLA
+ xla_zero();
+#endif
+
+ while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
+ {
+ if (bp[0] == '#')
+ {
+ if (bp != buf)
+ free(bp);
+ continue;
+ }
+
+ /* do macro expansion mappings */
+ translate_dollars(bp);
+
+ /* interpret this line */
+ errno = 0;
+ switch (bp[0])
+ {
+ case '\0':
+ case '#': /* comment */
+ break;
+
+ case 'R': /* rewriting rule */
+ for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
+ continue;
+
+ if (*p == '\0')
+ {
+ syserr("invalid rewrite line \"%s\" (tab expected)", bp);
+ break;
+ }
+
+ /* allocate space for the rule header */
+ if (rwp == NULL)
+ {
+ RewriteRules[ruleset] = rwp =
+ (struct rewrite *) xalloc(sizeof *rwp);
+ }
+ else
+ {
+ rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
+ rwp = rwp->r_next;
+ }
+ rwp->r_next = NULL;
+
+ /* expand and save the LHS */
+ *p = '\0';
+ expand(&bp[1], exbuf, sizeof exbuf, e);
+ rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
+ sizeof pvpbuf, NULL, NULL);
+ nfuzzy = 0;
+ if (rwp->r_lhs != NULL)
+ {
+ register char **ap;
+
+ rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
+
+ /* count the number of fuzzy matches in LHS */
+ for (ap = rwp->r_lhs; *ap != NULL; ap++)
+ {
+ char *botch;
+
+ botch = NULL;
+ switch (**ap & 0377)
+ {
+ case MATCHZANY:
+ case MATCHANY:
+ case MATCHONE:
+ case MATCHCLASS:
+ case MATCHNCLASS:
+ nfuzzy++;
+ break;
+
+ case MATCHREPL:
+ botch = "$0-$9";
+ break;
+
+ case CANONUSER:
+ botch = "$:";
+ break;
+
+ case CALLSUBR:
+ botch = "$>";
+ break;
+
+ case CONDIF:
+ botch = "$?";
+ break;
+
+ case CONDFI:
+ botch = "$.";
+ break;
+
+ case HOSTBEGIN:
+ botch = "$[";
+ break;
+
+ case HOSTEND:
+ botch = "$]";
+ break;
+
+ case LOOKUPBEGIN:
+ botch = "$(";
+ break;
+
+ case LOOKUPEND:
+ botch = "$)";
+ break;
+ }
+ if (botch != NULL)
+ syserr("Inappropriate use of %s on LHS",
+ botch);
+ }
+ }
+ else
+ {
+ syserr("R line: null LHS");
+ rwp->r_lhs = null_list;
+ }
+
+ /* expand and save the RHS */
+ while (*++p == '\t')
+ continue;
+ q = p;
+ while (*p != '\0' && *p != '\t')
+ p++;
+ *p = '\0';
+ expand(q, exbuf, sizeof exbuf, e);
+ rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
+ sizeof pvpbuf, NULL, NULL);
+ if (rwp->r_rhs != NULL)
+ {
+ register char **ap;
+
+ rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
+
+ /* check no out-of-bounds replacements */
+ nfuzzy += '0';
+ for (ap = rwp->r_rhs; *ap != NULL; ap++)
+ {
+ char *botch;
+
+ botch = NULL;
+ switch (**ap & 0377)
+ {
+ case MATCHREPL:
+ if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
+ {
+ syserr("replacement $%c out of bounds",
+ (*ap)[1]);
+ }
+ break;
+
+ case MATCHZANY:
+ botch = "$*";
+ break;
+
+ case MATCHANY:
+ botch = "$+";
+ break;
+
+ case MATCHONE:
+ botch = "$-";
+ break;
+
+ case MATCHCLASS:
+ botch = "$=";
+ break;
+
+ case MATCHNCLASS:
+ botch = "$~";
+ break;
+ }
+ if (botch != NULL)
+ syserr("Inappropriate use of %s on RHS",
+ botch);
+ }
+ }
+ else
+ {
+ syserr("R line: null RHS");
+ rwp->r_rhs = null_list;
+ }
+ break;
+
+ case 'S': /* select rewriting set */
+ expand(&bp[1], exbuf, sizeof exbuf, e);
+ ruleset = strtorwset(exbuf, NULL, ST_ENTER);
+ if (ruleset < 0)
+ break;
+ rwp = RewriteRules[ruleset];
+ if (rwp != NULL)
+ {
+ if (OpMode == MD_TEST || tTd(37, 1))
+ printf("WARNING: Ruleset %s has multiple definitions\n",
+ &bp[1]);
+ while (rwp->r_next != NULL)
+ rwp = rwp->r_next;
+ }
+ break;
+
+ case 'D': /* macro definition */
+ mid = macid(&bp[1], &ep);
+ p = munchstring(ep, NULL, '\0');
+ define(mid, newstr(p), e);
+ break;
+
+ case 'H': /* required header line */
+ (void) chompheader(&bp[1], TRUE, NULL, e);
+ break;
+
+ case 'C': /* word class */
+ case 'T': /* trusted user (set class `t') */
+ if (bp[0] == 'C')
+ {
+ mid = macid(&bp[1], &ep);
+ expand(ep, exbuf, sizeof exbuf, e);
+ p = exbuf;
+ }
+ else
+ {
+ mid = 't';
+ p = &bp[1];
+ }
+ while (*p != '\0')
+ {
+ register char *wd;
+ char delim;
+
+ while (*p != '\0' && isascii(*p) && isspace(*p))
+ p++;
+ wd = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ delim = *p;
+ *p = '\0';
+ if (wd[0] != '\0')
+ setclass(mid, wd);
+ *p = delim;
+ }
+ break;
+
+ case 'F': /* word class from file */
+ mid = macid(&bp[1], &ep);
+ for (p = ep; isascii(*p) && isspace(*p); )
+ p++;
+ if (p[0] == '-' && p[1] == 'o')
+ {
+ optional = TRUE;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ while (isascii(*p) && isspace(*p))
+ p++;
+ }
+ else
+ optional = FALSE;
+ file = p;
+ if (*file == '|')
+ p = "%s";
+ else
+ {
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p == '\0')
+ p = "%s";
+ else
+ {
+ *p = '\0';
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ }
+ }
+ fileclass(mid, file, p, safe, optional);
+ break;
+
+#ifdef XLA
+ case 'L': /* extended load average description */
+ xla_init(&bp[1]);
+ break;
+#endif
+
+#if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
+ case 'L': /* lookup macro */
+ case 'G': /* lookup class */
+ /* reserved for Sun -- NIS+ database lookup */
+ if (VendorCode != VENDOR_SUN)
+ goto badline;
+ sun_lg_config_line(bp, e);
+ break;
+#endif
+
+ case 'M': /* define mailer */
+ makemailer(&bp[1]);
+ break;
+
+ case 'O': /* set option */
+ setoption(bp[1], &bp[2], safe, FALSE, e);
+ break;
+
+ case 'P': /* set precedence */
+ if (NumPriorities >= MAXPRIORITIES)
+ {
+ toomany('P', MAXPRIORITIES);
+ break;
+ }
+ for (p = &bp[1]; *p != '\0' && *p != '='; p++)
+ continue;
+ if (*p == '\0')
+ goto badline;
+ *p = '\0';
+ Priorities[NumPriorities].pri_name = newstr(&bp[1]);
+ Priorities[NumPriorities].pri_val = atoi(++p);
+ NumPriorities++;
+ break;
+
+ case 'V': /* configuration syntax version */
+ for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
+ continue;
+ if (!isascii(*p) || !isdigit(*p))
+ {
+ syserr("invalid argument to V line: \"%.20s\"",
+ &bp[1]);
+ break;
+ }
+ ConfigLevel = strtol(p, &ep, 10);
+
+ /*
+ ** Do heuristic tweaking for back compatibility.
+ */
+
+ if (ConfigLevel >= 5)
+ {
+ /* level 5 configs have short name in $w */
+ p = macvalue('w', e);
+ if (p != NULL && (p = strchr(p, '.')) != NULL)
+ *p = '\0';
+ define('w', macvalue('w', e), e);
+ }
+ if (ConfigLevel >= 6)
+ {
+ ColonOkInAddr = FALSE;
+ }
+
+ /*
+ ** Look for vendor code.
+ */
+
+ if (*ep++ == '/')
+ {
+ extern bool setvendor __P((char *));
+
+ /* extract vendor code */
+ for (p = ep; isascii(*p) && isalpha(*p); )
+ p++;
+ *p = '\0';
+
+ if (!setvendor(ep))
+ syserr("invalid V line vendor code: \"%s\"",
+ ep);
+ }
+ break;
+
+ case 'K':
+ expand(&bp[1], exbuf, sizeof exbuf, e);
+ (void) makemapentry(exbuf);
+ break;
+
+ case 'E':
+ p = strchr(bp, '=');
+ if (p != NULL)
+ *p++ = '\0';
+ setuserenv(&bp[1], p);
+ break;
+
+ default:
+ badline:
+ syserr("unknown configuration line \"%s\"", bp);
+ }
+ if (bp != buf)
+ free(bp);
+ }
+ if (ferror(cf))
+ {
+ syserr("I/O read error");
+ exit(EX_OSFILE);
+ }
+ fclose(cf);
+ FileName = NULL;
+
+ /* initialize host maps from local service tables */
+ inithostmaps();
+
+ /* determine if we need to do special name-server frotz */
+ {
+ int nmaps;
+ char *maptype[MAXMAPSTACK];
+ short mapreturn[MAXMAPACTIONS];
+
+ nmaps = switch_map_find("hosts", maptype, mapreturn);
+ UseNameServer = FALSE;
+ if (nmaps > 0 && nmaps <= MAXMAPSTACK)
+ {
+ register int mapno;
+
+ for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
+ {
+ if (strcmp(maptype[mapno], "dns") == 0)
+ UseNameServer = TRUE;
+ }
+ }
+
+#ifdef HESIOD
+ nmaps = switch_map_find("passwd", maptype, mapreturn);
+ UseHesiod = FALSE;
+ if (nmaps > 0 && nmaps <= MAXMAPSTACK)
+ {
+ register int mapno;
+
+ for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
+ {
+ if (strcmp(maptype[mapno], "hesiod") == 0)
+ UseHesiod = TRUE;
+ }
+ }
+#endif
+ }
+}
+ /*
+** TRANSLATE_DOLLARS -- convert $x into internal form
+**
+** Actually does all appropriate pre-processing of a config line
+** to turn it into internal form.
+**
+** Parameters:
+** bp -- the buffer to translate.
+**
+** Returns:
+** None. The buffer is translated in place. Since the
+** translations always make the buffer shorter, this is
+** safe without a size parameter.
+*/
+
+void
+translate_dollars(bp)
+ char *bp;
+{
+ register char *p;
+ auto char *ep;
+
+ for (p = bp; *p != '\0'; p++)
+ {
+ if (*p == '#' && p > bp && ConfigLevel >= 3)
+ {
+ /* this is an on-line comment */
+ register char *e;
+
+ switch (*--p & 0377)
+ {
+ case MACROEXPAND:
+ /* it's from $# -- let it go through */
+ p++;
+ break;
+
+ case '\\':
+ /* it's backslash escaped */
+ (void) strcpy(p, p + 1);
+ break;
+
+ default:
+ /* delete preceeding white space */
+ while (isascii(*p) && isspace(*p) &&
+ *p != '\n' && p > bp)
+ p--;
+ if ((e = strchr(++p, '\n')) != NULL)
+ (void) strcpy(p, e);
+ else
+ *p-- = '\0';
+ break;
+ }
+ continue;
+ }
+
+ if (*p != '$' || p[1] == '\0')
+ continue;
+
+ if (p[1] == '$')
+ {
+ /* actual dollar sign.... */
+ (void) strcpy(p, p + 1);
+ continue;
+ }
+
+ /* convert to macro expansion character */
+ *p++ = MACROEXPAND;
+
+ /* special handling for $=, $~, $&, and $? */
+ if (*p == '=' || *p == '~' || *p == '&' || *p == '?')
+ p++;
+
+ /* convert macro name to code */
+ *p = macid(p, &ep);
+ if (ep != p)
+ strcpy(p + 1, ep);
+ }
+
+ /* strip trailing white space from the line */
+ while (--p > bp && isascii(*p) && isspace(*p))
+ *p = '\0';
+}
+ /*
+** TOOMANY -- signal too many of some option
+**
+** Parameters:
+** id -- the id of the error line
+** maxcnt -- the maximum possible values
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** gives a syserr.
+*/
+
+void
+toomany(id, maxcnt)
+ int id;
+ int maxcnt;
+{
+ syserr("too many %c lines, %d max", id, maxcnt);
+}
+ /*
+** FILECLASS -- read members of a class from a file
+**
+** Parameters:
+** class -- class to define.
+** filename -- name of file to read.
+** fmt -- scanf string to use for match.
+** safe -- if set, this is a safe read.
+** optional -- if set, it is not an error for the file to
+** not exist.
+**
+** Returns:
+** none
+**
+** Side Effects:
+**
+** puts all lines in filename that match a scanf into
+** the named class.
+*/
+
+void
+fileclass(class, filename, fmt, safe, optional)
+ int class;
+ char *filename;
+ char *fmt;
+ bool safe;
+ bool optional;
+{
+ FILE *f;
+ int sff;
+ pid_t pid;
+ register char *p;
+ char buf[MAXLINE];
+
+ if (tTd(37, 2))
+ printf("fileclass(%s, fmt=%s)\n", filename, fmt);
+
+ if (filename[0] == '|')
+ {
+ auto int fd;
+ int i;
+ char *argv[MAXPV + 1];
+
+ i = 0;
+ for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
+ {
+ if (i >= MAXPV)
+ break;
+ argv[i++] = p;
+ }
+ argv[i] = NULL;
+ pid = prog_open(argv, &fd, CurEnv);
+ if (pid < 0)
+ f = NULL;
+ else
+ f = fdopen(fd, "r");
+ }
+ else
+ {
+ pid = -1;
+ sff = SFF_REGONLY;
+ if (!bitset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
+ sff |= SFF_SAFEDIRPATH;
+ if (!bitset(DBS_LINKEDCLASSFILEINWRITABLEDIR, DontBlameSendmail))
+ sff |= SFF_NOWLINK;
+ if (safe)
+ sff |= SFF_OPENASROOT;
+ if (DontLockReadFiles)
+ sff |= SFF_NOLOCK;
+ f = safefopen(filename, O_RDONLY, 0, sff);
+ }
+ if (f == NULL)
+ {
+ if (!optional)
+ syserr("fileclass: cannot open %s", filename);
+ return;
+ }
+
+ while (fgets(buf, sizeof buf, f) != NULL)
+ {
+ register char *p;
+# if SCANF
+ char wordbuf[MAXLINE + 1];
+# endif
+
+ if (buf[0] == '#')
+ continue;
+# if SCANF
+ if (sscanf(buf, fmt, wordbuf) != 1)
+ continue;
+ p = wordbuf;
+# else /* SCANF */
+ p = buf;
+# endif /* SCANF */
+
+ /*
+ ** Break up the match into words.
+ */
+
+ while (*p != '\0')
+ {
+ register char *q;
+
+ /* strip leading spaces */
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+
+ /* find the end of the word */
+ q = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+
+ /* enter the word in the symbol table */
+ setclass(class, q);
+ }
+ }
+
+ (void) fclose(f);
+ if (pid > 0)
+ (void) waitfor(pid);
+}
+ /*
+** MAKEMAILER -- define a new mailer.
+**
+** Parameters:
+** line -- description of mailer. This is in labeled
+** fields. The fields are:
+** A -- the argv for this mailer
+** C -- the character set for MIME conversions
+** D -- the directory to run in
+** E -- the eol string
+** F -- the flags associated with the mailer
+** L -- the maximum line length
+** M -- the maximum message size
+** N -- the niceness at which to run
+** P -- the path to the mailer
+** R -- the recipient rewriting set
+** S -- the sender rewriting set
+** T -- the mailer type (for DSNs)
+** U -- the uid to run as
+** The first word is the canonical name of the mailer.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** enters the mailer into the mailer table.
+*/
+
+void
+makemailer(line)
+ char *line;
+{
+ register char *p;
+ register struct mailer *m;
+ register STAB *s;
+ int i;
+ char fcode;
+ auto char *endp;
+ extern int NextMailer;
+ extern char **makeargv __P((char *));
+ extern char *munchstring __P((char *, char **, int));
+
+ /* allocate a mailer and set up defaults */
+ m = (struct mailer *) xalloc(sizeof *m);
+ bzero((char *) m, sizeof *m);
+
+ /* collect the mailer name */
+ for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
+ continue;
+ if (*p != '\0')
+ *p++ = '\0';
+ if (line[0] == '\0')
+ syserr("name required for mailer");
+ m->m_name = newstr(line);
+
+ /* now scan through and assign info from the fields */
+ while (*p != '\0')
+ {
+ auto char *delimptr;
+
+ while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
+ p++;
+
+ /* p now points to field code */
+ fcode = *p;
+ while (*p != '\0' && *p != '=' && *p != ',')
+ p++;
+ if (*p++ != '=')
+ {
+ syserr("mailer %s: `=' expected", m->m_name);
+ return;
+ }
+ while (isascii(*p) && isspace(*p))
+ p++;
+
+ /* p now points to the field body */
+ p = munchstring(p, &delimptr, ',');
+
+ /* install the field into the mailer struct */
+ switch (fcode)
+ {
+ case 'P': /* pathname */
+ if (*p == '\0')
+ syserr("mailer %s: empty path name", m->m_name);
+ m->m_mailer = newstr(p);
+ break;
+
+ case 'F': /* flags */
+ for (; *p != '\0'; p++)
+ if (!(isascii(*p) && isspace(*p)))
+ setbitn(*p, m->m_flags);
+ break;
+
+ case 'S': /* sender rewriting ruleset */
+ case 'R': /* recipient rewriting ruleset */
+ i = strtorwset(p, &endp, ST_ENTER);
+ if (i < 0)
+ return;
+ if (fcode == 'S')
+ m->m_sh_rwset = m->m_se_rwset = i;
+ else
+ m->m_rh_rwset = m->m_re_rwset = i;
+
+ p = endp;
+ if (*p++ == '/')
+ {
+ i = strtorwset(p, NULL, ST_ENTER);
+ if (i < 0)
+ return;
+ if (fcode == 'S')
+ m->m_sh_rwset = i;
+ else
+ m->m_rh_rwset = i;
+ }
+ break;
+
+ case 'E': /* end of line string */
+ if (*p == '\0')
+ syserr("mailer %s: null end-of-line string",
+ m->m_name);
+ m->m_eol = newstr(p);
+ break;
+
+ case 'A': /* argument vector */
+ if (*p == '\0')
+ syserr("mailer %s: null argument vector",
+ m->m_name);
+ m->m_argv = makeargv(p);
+ break;
+
+ case 'M': /* maximum message size */
+ m->m_maxsize = atol(p);
+ break;
+
+ case 'L': /* maximum line length */
+ m->m_linelimit = atoi(p);
+ if (m->m_linelimit < 0)
+ m->m_linelimit = 0;
+ break;
+
+ case 'N': /* run niceness */
+ m->m_nice = atoi(p);
+ break;
+
+ case 'D': /* working directory */
+ if (*p == '\0')
+ syserr("mailer %s: null working directory",
+ m->m_name);
+ m->m_execdir = newstr(p);
+ break;
+
+ case 'C': /* default charset */
+ if (*p == '\0')
+ syserr("mailer %s: null charset", m->m_name);
+ m->m_defcharset = newstr(p);
+ break;
+
+ case 'T': /* MTA-Name/Address/Diagnostic types */
+ /* extract MTA name type; default to "dns" */
+ m->m_mtatype = newstr(p);
+ p = strchr(m->m_mtatype, '/');
+ if (p != NULL)
+ {
+ *p++ = '\0';
+ if (*p == '\0')
+ p = NULL;
+ }
+ if (*m->m_mtatype == '\0')
+ m->m_mtatype = "dns";
+
+ /* extract address type; default to "rfc822" */
+ m->m_addrtype = p;
+ if (p != NULL)
+ p = strchr(p, '/');
+ if (p != NULL)
+ {
+ *p++ = '\0';
+ if (*p == '\0')
+ p = NULL;
+ }
+ if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
+ m->m_addrtype = "rfc822";
+
+ /* extract diagnostic type; default to "smtp" */
+ m->m_diagtype = p;
+ if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
+ m->m_diagtype = "smtp";
+ break;
+
+ case 'U': /* user id */
+ if (isascii(*p) && !isdigit(*p))
+ {
+ char *q = p;
+ struct passwd *pw;
+
+ while (*p != '\0' && isascii(*p) &&
+ (isalnum(*p) || strchr("-_", *p) != NULL))
+ p++;
+ while (isascii(*p) && isspace(*p))
+ *p++ = '\0';
+ if (*p != '\0')
+ *p++ = '\0';
+ if (*q == '\0')
+ syserr("mailer %s: null user name",
+ m->m_name);
+ pw = sm_getpwnam(q);
+ if (pw == NULL)
+ syserr("readcf: mailer U= flag: unknown user %s", q);
+ else
+ {
+ m->m_uid = pw->pw_uid;
+ m->m_gid = pw->pw_gid;
+ }
+ }
+ else
+ {
+ auto char *q;
+
+ m->m_uid = strtol(p, &q, 0);
+ p = q;
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p != '\0')
+ p++;
+ }
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ if (isascii(*p) && !isdigit(*p))
+ {
+ char *q = p;
+ struct group *gr;
+
+ while (isascii(*p) && isalnum(*p))
+ p++;
+ *p++ = '\0';
+ if (*q == '\0')
+ syserr("mailer %s: null group name",
+ m->m_name);
+ gr = getgrnam(q);
+ if (gr == NULL)
+ syserr("readcf: mailer U= flag: unknown group %s", q);
+ else
+ m->m_gid = gr->gr_gid;
+ }
+ else
+ {
+ m->m_gid = strtol(p, NULL, 0);
+ }
+ break;
+ }
+
+ p = delimptr;
+ }
+
+ /* do some rationality checking */
+ if (m->m_argv == NULL)
+ {
+ syserr("M%s: A= argument required", m->m_name);
+ return;
+ }
+ if (m->m_mailer == NULL)
+ {
+ syserr("M%s: P= argument required", m->m_name);
+ return;
+ }
+
+ if (NextMailer >= MAXMAILERS)
+ {
+ syserr("too many mailers defined (%d max)", MAXMAILERS);
+ return;
+ }
+
+ /* do some heuristic cleanup for back compatibility */
+ if (bitnset(M_LIMITS, m->m_flags))
+ {
+ if (m->m_linelimit == 0)
+ m->m_linelimit = SMTPLINELIM;
+ if (ConfigLevel < 2)
+ setbitn(M_7BITS, m->m_flags);
+ }
+
+ if (strcmp(m->m_mailer, "[IPC]") == 0 ||
+ strcmp(m->m_mailer, "[TCP]") == 0)
+ {
+ if (m->m_mtatype == NULL)
+ m->m_mtatype = "dns";
+ if (m->m_addrtype == NULL)
+ m->m_addrtype = "rfc822";
+ if (m->m_diagtype == NULL)
+ m->m_diagtype = "smtp";
+ }
+
+ if (strcmp(m->m_mailer, "[FILE]") == 0)
+ {
+ /* Use the second argument for filename */
+ if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
+ m->m_argv[2] != NULL)
+ {
+ syserr("M%s: too %s parameters for [FILE] mailer",
+ m->m_name,
+ (m->m_argv[0] == NULL ||
+ m->m_argv[1] == NULL) ? "few" : "many");
+ }
+ else if (strcmp(m->m_argv[0], "FILE") != 0)
+ {
+ syserr("M%s: first argument in [FILE] mailer must be FILE",
+ m->m_name);
+ }
+ }
+
+ if (m->m_eol == NULL)
+ {
+ char **pp;
+
+ /* default for SMTP is \r\n; use \n for local delivery */
+ for (pp = m->m_argv; *pp != NULL; pp++)
+ {
+ char *p;
+
+ for (p = *pp; *p != '\0'; )
+ {
+ if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
+ break;
+ }
+ if (*p != '\0')
+ break;
+ }
+ if (*pp == NULL)
+ m->m_eol = "\r\n";
+ else
+ m->m_eol = "\n";
+ }
+
+ /* enter the mailer into the symbol table */
+ s = stab(m->m_name, ST_MAILER, ST_ENTER);
+ if (s->s_mailer != NULL)
+ {
+ i = s->s_mailer->m_mno;
+ free(s->s_mailer);
+ }
+ else
+ {
+ i = NextMailer++;
+ }
+ Mailer[i] = s->s_mailer = m;
+ m->m_mno = i;
+}
+ /*
+** MUNCHSTRING -- translate a string into internal form.
+**
+** Parameters:
+** p -- the string to munch.
+** delimptr -- if non-NULL, set to the pointer of the
+** field delimiter character.
+** delim -- the delimiter for the field.
+**
+** Returns:
+** the munched string.
+*/
+
+char *
+munchstring(p, delimptr, delim)
+ register char *p;
+ char **delimptr;
+ int delim;
+{
+ register char *q;
+ bool backslash = FALSE;
+ bool quotemode = FALSE;
+ static char buf[MAXLINE];
+
+ for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
+ {
+ if (backslash)
+ {
+ /* everything is roughly literal */
+ backslash = FALSE;
+ switch (*p)
+ {
+ case 'r': /* carriage return */
+ *q++ = '\r';
+ continue;
+
+ case 'n': /* newline */
+ *q++ = '\n';
+ continue;
+
+ case 'f': /* form feed */
+ *q++ = '\f';
+ continue;
+
+ case 'b': /* backspace */
+ *q++ = '\b';
+ continue;
+ }
+ *q++ = *p;
+ }
+ else
+ {
+ if (*p == '\\')
+ backslash = TRUE;
+ else if (*p == '"')
+ quotemode = !quotemode;
+ else if (quotemode || *p != delim)
+ *q++ = *p;
+ else
+ break;
+ }
+ }
+
+ if (delimptr != NULL)
+ *delimptr = p;
+ *q++ = '\0';
+ return (buf);
+}
+ /*
+** MAKEARGV -- break up a string into words
+**
+** Parameters:
+** p -- the string to break up.
+**
+** Returns:
+** a char **argv (dynamically allocated)
+**
+** Side Effects:
+** munges p.
+*/
+
+char **
+makeargv(p)
+ register char *p;
+{
+ char *q;
+ int i;
+ char **avp;
+ char *argv[MAXPV + 1];
+
+ /* take apart the words */
+ i = 0;
+ while (*p != '\0' && i < MAXPV)
+ {
+ q = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ while (isascii(*p) && isspace(*p))
+ *p++ = '\0';
+ argv[i++] = newstr(q);
+ }
+ argv[i++] = NULL;
+
+ /* now make a copy of the argv */
+ avp = (char **) xalloc(sizeof *avp * i);
+ bcopy((char *) argv, (char *) avp, sizeof *avp * i);
+
+ return (avp);
+}
+ /*
+** PRINTRULES -- print rewrite rules (for debugging)
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** prints rewrite rules.
+*/
+
+void
+printrules()
+{
+ register struct rewrite *rwp;
+ register int ruleset;
+
+ for (ruleset = 0; ruleset < 10; ruleset++)
+ {
+ if (RewriteRules[ruleset] == NULL)
+ continue;
+ printf("\n----Rule Set %d:", ruleset);
+
+ for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
+ {
+ printf("\nLHS:");
+ printav(rwp->r_lhs);
+ printf("RHS:");
+ printav(rwp->r_rhs);
+ }
+ }
+}
+ /*
+** PRINTMAILER -- print mailer structure (for debugging)
+**
+** Parameters:
+** m -- the mailer to print
+**
+** Returns:
+** none.
+*/
+
+void
+printmailer(m)
+ register MAILER *m;
+{
+ int j;
+
+ printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
+ m->m_mno, m->m_name,
+ m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
+ m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
+ (int) m->m_uid, (int) m->m_gid);
+ for (j = '\0'; j <= '\177'; j++)
+ if (bitnset(j, m->m_flags))
+ (void) putchar(j);
+ printf(" L=%d E=", m->m_linelimit);
+ xputs(m->m_eol);
+ if (m->m_defcharset != NULL)
+ printf(" C=%s", m->m_defcharset);
+ printf(" T=%s/%s/%s",
+ m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
+ m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
+ m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
+ if (m->m_argv != NULL)
+ {
+ char **a = m->m_argv;
+
+ printf(" A=");
+ while (*a != NULL)
+ {
+ if (a != m->m_argv)
+ printf(" ");
+ xputs(*a++);
+ }
+ }
+ printf("\n");
+}
+ /*
+** SETOPTION -- set global processing option
+**
+** Parameters:
+** opt -- option name.
+** val -- option value (as a text string).
+** safe -- set if this came from a configuration file.
+** Some options (if set from the command line) will
+** reset the user id to avoid security problems.
+** sticky -- if set, don't let other setoptions override
+** this value.
+** e -- the main envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets options as implied by the arguments.
+*/
+
+static BITMAP StickyOpt; /* set if option is stuck */
+extern void settimeout __P((char *, char *));
+
+
+#if NAMED_BIND
+
+struct resolverflags
+{
+ char *rf_name; /* name of the flag */
+ long rf_bits; /* bits to set/clear */
+} ResolverFlags[] =
+{
+ { "debug", RES_DEBUG },
+ { "aaonly", RES_AAONLY },
+ { "usevc", RES_USEVC },
+ { "primary", RES_PRIMARY },
+ { "igntc", RES_IGNTC },
+ { "recurse", RES_RECURSE },
+ { "defnames", RES_DEFNAMES },
+ { "stayopen", RES_STAYOPEN },
+ { "dnsrch", RES_DNSRCH },
+ { "true", 0 }, /* avoid error on old syntax */
+ { NULL, 0 }
+};
+
+#endif
+
+struct optioninfo
+{
+ char *o_name; /* long name of option */
+ u_char o_code; /* short name of option */
+ bool o_safe; /* safe for random people to use */
+} OptionTab[] =
+{
+ { "SevenBitInput", '7', TRUE },
+#if MIME8TO7
+ { "EightBitMode", '8', TRUE },
+#endif
+ { "AliasFile", 'A', FALSE },
+ { "AliasWait", 'a', FALSE },
+ { "BlankSub", 'B', FALSE },
+ { "MinFreeBlocks", 'b', TRUE },
+ { "CheckpointInterval", 'C', TRUE },
+ { "HoldExpensive", 'c', FALSE },
+ { "AutoRebuildAliases", 'D', FALSE },
+ { "DeliveryMode", 'd', TRUE },
+ { "ErrorHeader", 'E', FALSE },
+ { "ErrorMode", 'e', TRUE },
+ { "TempFileMode", 'F', FALSE },
+ { "SaveFromLine", 'f', FALSE },
+ { "MatchGECOS", 'G', FALSE },
+ { "HelpFile", 'H', FALSE },
+ { "MaxHopCount", 'h', FALSE },
+ { "ResolverOptions", 'I', FALSE },
+ { "IgnoreDots", 'i', TRUE },
+ { "ForwardPath", 'J', FALSE },
+ { "SendMimeErrors", 'j', TRUE },
+ { "ConnectionCacheSize", 'k', FALSE },
+ { "ConnectionCacheTimeout", 'K', FALSE },
+ { "UseErrorsTo", 'l', FALSE },
+ { "LogLevel", 'L', TRUE },
+ { "MeToo", 'm', TRUE },
+ { "CheckAliases", 'n', FALSE },
+ { "OldStyleHeaders", 'o', TRUE },
+ { "DaemonPortOptions", 'O', FALSE },
+ { "PrivacyOptions", 'p', TRUE },
+ { "PostmasterCopy", 'P', FALSE },
+ { "QueueFactor", 'q', FALSE },
+ { "QueueDirectory", 'Q', FALSE },
+ { "DontPruneRoutes", 'R', FALSE },
+ { "Timeout", 'r', FALSE },
+ { "StatusFile", 'S', FALSE },
+ { "SuperSafe", 's', TRUE },
+ { "QueueTimeout", 'T', FALSE },
+ { "TimeZoneSpec", 't', FALSE },
+ { "UserDatabaseSpec", 'U', FALSE },
+ { "DefaultUser", 'u', FALSE },
+ { "FallbackMXhost", 'V', FALSE },
+ { "Verbose", 'v', TRUE },
+ { "TryNullMXList", 'w', FALSE },
+ { "QueueLA", 'x', FALSE },
+ { "RefuseLA", 'X', FALSE },
+ { "RecipientFactor", 'y', FALSE },
+ { "ForkEachJob", 'Y', FALSE },
+ { "ClassFactor", 'z', FALSE },
+ { "RetryFactor", 'Z', FALSE },
+#define O_QUEUESORTORD 0x81
+ { "QueueSortOrder", O_QUEUESORTORD, TRUE },
+#define O_HOSTSFILE 0x82
+ { "HostsFile", O_HOSTSFILE, FALSE },
+#define O_MQA 0x83
+ { "MinQueueAge", O_MQA, TRUE },
+#define O_DEFCHARSET 0x85
+ { "DefaultCharSet", O_DEFCHARSET, TRUE },
+#define O_SSFILE 0x86
+ { "ServiceSwitchFile", O_SSFILE, FALSE },
+#define O_DIALDELAY 0x87
+ { "DialDelay", O_DIALDELAY, TRUE },
+#define O_NORCPTACTION 0x88
+ { "NoRecipientAction", O_NORCPTACTION, TRUE },
+#define O_SAFEFILEENV 0x89
+ { "SafeFileEnvironment", O_SAFEFILEENV, FALSE },
+#define O_MAXMSGSIZE 0x8a
+ { "MaxMessageSize", O_MAXMSGSIZE, FALSE },
+#define O_COLONOKINADDR 0x8b
+ { "ColonOkInAddr", O_COLONOKINADDR, TRUE },
+#define O_MAXQUEUERUN 0x8c
+ { "MaxQueueRunSize", O_MAXQUEUERUN, TRUE },
+#define O_MAXCHILDREN 0x8d
+ { "MaxDaemonChildren", O_MAXCHILDREN, FALSE },
+#define O_KEEPCNAMES 0x8e
+ { "DontExpandCnames", O_KEEPCNAMES, FALSE },
+#define O_MUSTQUOTE 0x8f
+ { "MustQuoteChars", O_MUSTQUOTE, FALSE },
+#define O_SMTPGREETING 0x90
+ { "SmtpGreetingMessage", O_SMTPGREETING, FALSE },
+#define O_UNIXFROM 0x91
+ { "UnixFromLine", O_UNIXFROM, FALSE },
+#define O_OPCHARS 0x92
+ { "OperatorChars", O_OPCHARS, FALSE },
+#define O_DONTINITGRPS 0x93
+ { "DontInitGroups", O_DONTINITGRPS, FALSE },
+#define O_SLFH 0x94
+ { "SingleLineFromHeader", O_SLFH, TRUE },
+#define O_ABH 0x95
+ { "AllowBogusHELO", O_ABH, TRUE },
+#define O_CONNTHROT 0x97
+ { "ConnectionRateThrottle", O_CONNTHROT, FALSE },
+#define O_UGW 0x99
+ { "UnsafeGroupWrites", O_UGW, FALSE },
+#define O_DBLBOUNCE 0x9a
+ { "DoubleBounceAddress", O_DBLBOUNCE, FALSE },
+#define O_HSDIR 0x9b
+ { "HostStatusDirectory", O_HSDIR, FALSE },
+#define O_SINGTHREAD 0x9c
+ { "SingleThreadDelivery", O_SINGTHREAD, FALSE },
+#define O_RUNASUSER 0x9d
+ { "RunAsUser", O_RUNASUSER, FALSE },
+#if _FFR_DSN_RRT_OPTION
+#define O_DSN_RRT 0x9e
+ { "RrtImpliesDsn", O_DSN_RRT, FALSE },
+#endif
+#if _FFR_PIDFILE_OPTION
+#define O_PIDFILE 0x9f
+ { "PidFile", O_PIDFILE, FALSE },
+#endif
+#define O_DONTBLAMESENDMAIL 0xa0
+ { "DontBlameSendmail", O_DONTBLAMESENDMAIL, FALSE },
+#define O_DPI 0xa1
+ { "DontProbeInterfaces", O_DPI, FALSE },
+#define O_MAXRCPT 0xa2
+ { "MaxRecipientsPerMessage", O_MAXRCPT, FALSE },
+#if _FFR_DEADLETTERDROP_OPTION
+#define O_DEADLETTER 0xa3
+ { "DeadLetterDrop", O_DEADLETTER, FALSE },
+#endif
+#if _FFR_DONTLOCKFILESFORREAD_OPTION
+#define O_DONTLOCK 0xa4
+ { "DontLockFilesForRead", O_DONTLOCK, FALSE },
+#endif
+#if _FFR_MAXALIASRECURSION_OPTION
+#define O_MAXALIASRCSN 0xa5
+ { "MaxAliasRecursion", O_MAXALIASRCSN, FALSE },
+#endif
+#if _FFR_CONNECTONLYTO_OPTION
+#define O_CNCTONLYTO 0xa6
+ { "ConnectOnlyTo", O_CNCTONLYTO, FALSE },
+#endif
+#if _FFR_TRUSTED_FILE_OWNER
+#define O_TRUSTFILEOWN 0xa7
+ { "TrustedFileOwner", O_TRUSTFILEOWN, FALSE },
+#endif
+
+ { NULL, '\0', FALSE }
+};
+
+
+
+void
+setoption(opt, val, safe, sticky, e)
+ int opt;
+ char *val;
+ bool safe;
+ bool sticky;
+ register ENVELOPE *e;
+{
+ register char *p;
+ register struct optioninfo *o;
+ char *subopt;
+ int mid;
+ bool can_setuid = RunAsUid == 0;
+ auto char *ep;
+ char buf[50];
+ extern bool atobool __P((char *));
+ extern time_t convtime __P((char *, char));
+ extern int QueueLA;
+ extern int RefuseLA;
+ extern bool Warn_Q_option;
+ extern void setalias __P((char *));
+ extern int atooct __P((char *));
+ extern void setdefuser __P((void));
+ extern void setdaemonoptions __P((char *));
+
+ errno = 0;
+ if (opt == ' ')
+ {
+ /* full word options */
+ struct optioninfo *sel;
+
+ p = strchr(val, '=');
+ if (p == NULL)
+ p = &val[strlen(val)];
+ while (*--p == ' ')
+ continue;
+ while (*++p == ' ')
+ *p = '\0';
+ if (p == val)
+ {
+ syserr("readcf: null option name");
+ return;
+ }
+ if (*p == '=')
+ *p++ = '\0';
+ while (*p == ' ')
+ p++;
+ subopt = strchr(val, '.');
+ if (subopt != NULL)
+ *subopt++ = '\0';
+ sel = NULL;
+ for (o = OptionTab; o->o_name != NULL; o++)
+ {
+ if (strncasecmp(o->o_name, val, strlen(val)) != 0)
+ continue;
+ if (strlen(o->o_name) == strlen(val))
+ {
+ /* completely specified -- this must be it */
+ sel = NULL;
+ break;
+ }
+ if (sel != NULL)
+ break;
+ sel = o;
+ }
+ if (sel != NULL && o->o_name == NULL)
+ o = sel;
+ else if (o->o_name == NULL)
+ {
+ syserr("readcf: unknown option name %s", val);
+ return;
+ }
+ else if (sel != NULL)
+ {
+ syserr("readcf: ambiguous option name %s (matches %s and %s)",
+ val, sel->o_name, o->o_name);
+ return;
+ }
+ if (strlen(val) != strlen(o->o_name))
+ {
+ int oldVerbose = Verbose;
+
+ Verbose = 1;
+ message("Option %s used as abbreviation for %s",
+ val, o->o_name);
+ Verbose = oldVerbose;
+ }
+ opt = o->o_code;
+ val = p;
+ }
+ else
+ {
+ for (o = OptionTab; o->o_name != NULL; o++)
+ {
+ if (o->o_code == opt)
+ break;
+ }
+ subopt = NULL;
+ }
+
+ if (tTd(37, 1))
+ {
+ printf(isascii(opt) && isprint(opt) ?
+ "setoption %s (%c).%s=" :
+ "setoption %s (0x%x).%s=",
+ o->o_name == NULL ? "<unknown>" : o->o_name,
+ opt,
+ subopt == NULL ? "" : subopt);
+ xputs(val);
+ }
+
+ /*
+ ** See if this option is preset for us.
+ */
+
+ if (!sticky && bitnset(opt, StickyOpt))
+ {
+ if (tTd(37, 1))
+ printf(" (ignored)\n");
+ return;
+ }
+
+ /*
+ ** Check to see if this option can be specified by this user.
+ */
+
+ if (!safe && RealUid == 0)
+ safe = TRUE;
+ if (!safe && !o->o_safe)
+ {
+ if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
+ {
+ if (tTd(37, 1))
+ printf(" (unsafe)");
+ (void) drop_privileges(TRUE);
+ }
+ }
+ if (tTd(37, 1))
+ printf("\n");
+
+ switch (opt & 0xff)
+ {
+ case '7': /* force seven-bit input */
+ SevenBitInput = atobool(val);
+ break;
+
+#if MIME8TO7
+ case '8': /* handling of 8-bit input */
+ switch (*val)
+ {
+ case 'm': /* convert 8-bit, convert MIME */
+ MimeMode = MM_CVTMIME|MM_MIME8BIT;
+ break;
+
+ case 'p': /* pass 8 bit, convert MIME */
+ MimeMode = MM_CVTMIME|MM_PASS8BIT;
+ break;
+
+ case 's': /* strict adherence */
+ MimeMode = MM_CVTMIME;
+ break;
+
+#if 0
+ case 'r': /* reject 8-bit, don't convert MIME */
+ MimeMode = 0;
+ break;
+
+ case 'j': /* "just send 8" */
+ MimeMode = MM_PASS8BIT;
+ break;
+
+ case 'a': /* encode 8 bit if available */
+ MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
+ break;
+
+ case 'c': /* convert 8 bit to MIME, never 7 bit */
+ MimeMode = MM_MIME8BIT;
+ break;
+#endif
+
+ default:
+ syserr("Unknown 8-bit mode %c", *val);
+ exit(EX_USAGE);
+ }
+ break;
+#endif
+
+ case 'A': /* set default alias file */
+ if (val[0] == '\0')
+ setalias("aliases");
+ else
+ setalias(val);
+ break;
+
+ case 'a': /* look N minutes for "@:@" in alias file */
+ if (val[0] == '\0')
+ SafeAlias = 5 * 60; /* five minutes */
+ else
+ SafeAlias = convtime(val, 'm');
+ break;
+
+ case 'B': /* substitution for blank character */
+ SpaceSub = val[0];
+ if (SpaceSub == '\0')
+ SpaceSub = ' ';
+ break;
+
+ case 'b': /* min blocks free on queue fs/max msg size */
+ p = strchr(val, '/');
+ if (p != NULL)
+ {
+ *p++ = '\0';
+ MaxMessageSize = atol(p);
+ }
+ MinBlocksFree = atol(val);
+ break;
+
+ case 'c': /* don't connect to "expensive" mailers */
+ NoConnect = atobool(val);
+ break;
+
+ case 'C': /* checkpoint every N addresses */
+ CheckpointInterval = atoi(val);
+ break;
+
+ case 'd': /* delivery mode */
+ switch (*val)
+ {
+ case '\0':
+ e->e_sendmode = SM_DELIVER;
+ break;
+
+ case SM_QUEUE: /* queue only */
+ case SM_DEFER: /* queue only and defer map lookups */
+#if !QUEUE
+ syserr("need QUEUE to set -odqueue or -oddefer");
+#endif /* QUEUE */
+ /* fall through..... */
+
+ case SM_DELIVER: /* do everything */
+ case SM_FORK: /* fork after verification */
+ e->e_sendmode = *val;
+ break;
+
+ default:
+ syserr("Unknown delivery mode %c", *val);
+ exit(EX_USAGE);
+ }
+ buf[0] = (char)e->e_sendmode;
+ buf[1] = '\0';
+ define(macid("{deliveryMode}", NULL), newstr(buf), e);
+ break;
+
+ case 'D': /* rebuild alias database as needed */
+ AutoRebuild = atobool(val);
+ break;
+
+ case 'E': /* error message header/header file */
+ if (*val != '\0')
+ ErrMsgFile = newstr(val);
+ break;
+
+ case 'e': /* set error processing mode */
+ switch (*val)
+ {
+ case EM_QUIET: /* be silent about it */
+ case EM_MAIL: /* mail back */
+ case EM_BERKNET: /* do berknet error processing */
+ case EM_WRITE: /* write back (or mail) */
+ case EM_PRINT: /* print errors normally (default) */
+ e->e_errormode = *val;
+ break;
+ }
+ break;
+
+ case 'F': /* file mode */
+ FileMode = atooct(val) & 0777;
+ break;
+
+ case 'f': /* save Unix-style From lines on front */
+ SaveFrom = atobool(val);
+ break;
+
+ case 'G': /* match recipients against GECOS field */
+ MatchGecos = atobool(val);
+ break;
+
+ case 'g': /* default gid */
+ g_opt:
+ if (isascii(*val) && isdigit(*val))
+ DefGid = atoi(val);
+ else
+ {
+ register struct group *gr;
+
+ DefGid = -1;
+ gr = getgrnam(val);
+ if (gr == NULL)
+ syserr("readcf: option %c: unknown group %s",
+ opt, val);
+ else
+ DefGid = gr->gr_gid;
+ }
+ break;
+
+ case 'H': /* help file */
+ if (val[0] == '\0')
+ HelpFile = "sendmail.hf";
+ else
+ HelpFile = newstr(val);
+ break;
+
+ case 'h': /* maximum hop count */
+ MaxHopCount = atoi(val);
+ break;
+
+ case 'I': /* use internet domain name server */
+#if NAMED_BIND
+ for (p = val; *p != 0; )
+ {
+ bool clearmode;
+ char *q;
+ struct resolverflags *rfp;
+
+ while (*p == ' ')
+ p++;
+ if (*p == '\0')
+ break;
+ clearmode = FALSE;
+ if (*p == '-')
+ clearmode = TRUE;
+ else if (*p != '+')
+ p--;
+ p++;
+ q = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+ if (strcasecmp(q, "HasWildcardMX") == 0)
+ {
+ HasWildcardMX = !clearmode;
+ continue;
+ }
+ for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
+ {
+ if (strcasecmp(q, rfp->rf_name) == 0)
+ break;
+ }
+ if (rfp->rf_name == NULL)
+ syserr("readcf: I option value %s unrecognized", q);
+ else if (clearmode)
+ _res.options &= ~rfp->rf_bits;
+ else
+ _res.options |= rfp->rf_bits;
+ }
+ if (tTd(8, 2))
+ printf("_res.options = %x, HasWildcardMX = %d\n",
+ (u_int) _res.options, HasWildcardMX);
+#else
+ usrerr("name server (I option) specified but BIND not compiled in");
+#endif
+ break;
+
+ case 'i': /* ignore dot lines in message */
+ IgnrDot = atobool(val);
+ break;
+
+ case 'j': /* send errors in MIME (RFC 1341) format */
+ SendMIMEErrors = atobool(val);
+ break;
+
+ case 'J': /* .forward search path */
+ ForwardPath = newstr(val);
+ break;
+
+ case 'k': /* connection cache size */
+ MaxMciCache = atoi(val);
+ if (MaxMciCache < 0)
+ MaxMciCache = 0;
+ break;
+
+ case 'K': /* connection cache timeout */
+ MciCacheTimeout = convtime(val, 'm');
+ break;
+
+ case 'l': /* use Errors-To: header */
+ UseErrorsTo = atobool(val);
+ break;
+
+ case 'L': /* log level */
+ if (safe || LogLevel < atoi(val))
+ LogLevel = atoi(val);
+ break;
+
+ case 'M': /* define macro */
+ mid = macid(val, &ep);
+ p = newstr(ep);
+ if (!safe)
+ cleanstrcpy(p, p, MAXNAME);
+ define(mid, p, CurEnv);
+ sticky = FALSE;
+ break;
+
+ case 'm': /* send to me too */
+ MeToo = atobool(val);
+ break;
+
+ case 'n': /* validate RHS in newaliases */
+ CheckAliases = atobool(val);
+ break;
+
+ /* 'N' available -- was "net name" */
+
+ case 'O': /* daemon options */
+#if DAEMON
+ setdaemonoptions(val);
+#else
+ syserr("DaemonPortOptions (O option) set but DAEMON not compiled in");
+#endif
+ break;
+
+ case 'o': /* assume old style headers */
+ if (atobool(val))
+ CurEnv->e_flags |= EF_OLDSTYLE;
+ else
+ CurEnv->e_flags &= ~EF_OLDSTYLE;
+ break;
+
+ case 'p': /* select privacy level */
+ p = val;
+ for (;;)
+ {
+ register struct prival *pv;
+ extern struct prival PrivacyValues[];
+
+ while (isascii(*p) && (isspace(*p) || ispunct(*p)))
+ p++;
+ if (*p == '\0')
+ break;
+ val = p;
+ while (isascii(*p) && isalnum(*p))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+
+ for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
+ {
+ if (strcasecmp(val, pv->pv_name) == 0)
+ break;
+ }
+ if (pv->pv_name == NULL)
+ syserr("readcf: Op line: %s unrecognized", val);
+ PrivacyFlags |= pv->pv_flag;
+ }
+ sticky = FALSE;
+ break;
+
+ case 'P': /* postmaster copy address for returned mail */
+ PostMasterCopy = newstr(val);
+ break;
+
+ case 'q': /* slope of queue only function */
+ QueueFactor = atoi(val);
+ break;
+
+ case 'Q': /* queue directory */
+ if (val[0] == '\0')
+ QueueDir = "mqueue";
+ else
+ QueueDir = newstr(val);
+ if (RealUid != 0 && !safe)
+ Warn_Q_option = TRUE;
+ break;
+
+ case 'R': /* don't prune routes */
+ DontPruneRoutes = atobool(val);
+ break;
+
+ case 'r': /* read timeout */
+ if (subopt == NULL)
+ inittimeouts(val);
+ else
+ settimeout(subopt, val);
+ break;
+
+ case 'S': /* status file */
+ if (val[0] == '\0')
+ StatFile = "sendmail.st";
+ else
+ StatFile = newstr(val);
+ break;
+
+ case 's': /* be super safe, even if expensive */
+ SuperSafe = atobool(val);
+ break;
+
+ case 'T': /* queue timeout */
+ p = strchr(val, '/');
+ if (p != NULL)
+ {
+ *p++ = '\0';
+ settimeout("queuewarn", p);
+ }
+ settimeout("queuereturn", val);
+ break;
+
+ case 't': /* time zone name */
+ TimeZoneSpec = newstr(val);
+ break;
+
+ case 'U': /* location of user database */
+ UdbSpec = newstr(val);
+ break;
+
+ case 'u': /* set default uid */
+ for (p = val; *p != '\0'; p++)
+ {
+ if (*p == '.' || *p == '/' || *p == ':')
+ {
+ *p++ = '\0';
+ break;
+ }
+ }
+ if (isascii(*val) && isdigit(*val))
+ {
+ DefUid = atoi(val);
+ setdefuser();
+ }
+ else
+ {
+ register struct passwd *pw;
+
+ DefUid = -1;
+ pw = sm_getpwnam(val);
+ if (pw == NULL)
+ syserr("readcf: option u: unknown user %s", val);
+ else
+ {
+ DefUid = pw->pw_uid;
+ DefGid = pw->pw_gid;
+ DefUser = newstr(pw->pw_name);
+ }
+ }
+
+#ifdef UID_MAX
+ if (DefUid > UID_MAX)
+ {
+ syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
+ DefUid, UID_MAX);
+ }
+#endif
+
+ /* handle the group if it is there */
+ if (*p == '\0')
+ break;
+ val = p;
+ goto g_opt;
+
+ case 'V': /* fallback MX host */
+ if (val[0] != '\0')
+ FallBackMX = newstr(val);
+ break;
+
+ case 'v': /* run in verbose mode */
+ Verbose = atobool(val) ? 1 : 0;
+ break;
+
+ case 'w': /* if we are best MX, try host directly */
+ TryNullMXList = atobool(val);
+ break;
+
+ /* 'W' available -- was wizard password */
+
+ case 'x': /* load avg at which to auto-queue msgs */
+ QueueLA = atoi(val);
+ break;
+
+ case 'X': /* load avg at which to auto-reject connections */
+ RefuseLA = atoi(val);
+ break;
+
+ case 'y': /* work recipient factor */
+ WkRecipFact = atoi(val);
+ break;
+
+ case 'Y': /* fork jobs during queue runs */
+ ForkQueueRuns = atobool(val);
+ break;
+
+ case 'z': /* work message class factor */
+ WkClassFact = atoi(val);
+ break;
+
+ case 'Z': /* work time factor */
+ WkTimeFact = atoi(val);
+ break;
+
+ case O_QUEUESORTORD: /* queue sorting order */
+ switch (*val)
+ {
+ case 'h': /* Host first */
+ case 'H':
+ QueueSortOrder = QS_BYHOST;
+ break;
+
+ case 'p': /* Priority order */
+ case 'P':
+ QueueSortOrder = QS_BYPRIORITY;
+ break;
+
+ case 't': /* Submission time */
+ case 'T':
+ QueueSortOrder = QS_BYTIME;
+ break;
+
+ default:
+ syserr("Invalid queue sort order \"%s\"", val);
+ }
+ break;
+
+ case O_HOSTSFILE: /* pathname of /etc/hosts file */
+ HostsFile = newstr(val);
+ break;
+
+ case O_MQA: /* minimum queue age between deliveries */
+ MinQueueAge = convtime(val, 'm');
+ break;
+
+ case O_DEFCHARSET: /* default character set for mimefying */
+ DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
+ break;
+
+ case O_SSFILE: /* service switch file */
+ ServiceSwitchFile = newstr(val);
+ break;
+
+ case O_DIALDELAY: /* delay for dial-on-demand operation */
+ DialDelay = convtime(val, 's');
+ break;
+
+ case O_NORCPTACTION: /* what to do if no recipient */
+ if (strcasecmp(val, "none") == 0)
+ NoRecipientAction = NRA_NO_ACTION;
+ else if (strcasecmp(val, "add-to") == 0)
+ NoRecipientAction = NRA_ADD_TO;
+ else if (strcasecmp(val, "add-apparently-to") == 0)
+ NoRecipientAction = NRA_ADD_APPARENTLY_TO;
+ else if (strcasecmp(val, "add-bcc") == 0)
+ NoRecipientAction = NRA_ADD_BCC;
+ else if (strcasecmp(val, "add-to-undisclosed") == 0)
+ NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
+ else
+ syserr("Invalid NoRecipientAction: %s", val);
+ break;
+
+ case O_SAFEFILEENV: /* chroot() environ for writing to files */
+ SafeFileEnv = newstr(val);
+ break;
+
+ case O_MAXMSGSIZE: /* maximum message size */
+ MaxMessageSize = atol(val);
+ break;
+
+ case O_COLONOKINADDR: /* old style handling of colon addresses */
+ ColonOkInAddr = atobool(val);
+ break;
+
+ case O_MAXQUEUERUN: /* max # of jobs in a single queue run */
+ MaxQueueRun = atol(val);
+ break;
+
+ case O_MAXCHILDREN: /* max # of children of daemon */
+ MaxChildren = atoi(val);
+ break;
+
+ case O_KEEPCNAMES: /* don't expand CNAME records */
+ DontExpandCnames = atobool(val);
+ break;
+
+ case O_MUSTQUOTE: /* must quote these characters in phrases */
+ strcpy(buf, "@,;:\\()[]");
+ if (strlen(val) < (SIZE_T) sizeof buf - 10)
+ strcat(buf, val);
+ MustQuoteChars = newstr(buf);
+ break;
+
+ case O_SMTPGREETING: /* SMTP greeting message (old $e macro) */
+ SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
+ break;
+
+ case O_UNIXFROM: /* UNIX From_ line (old $l macro) */
+ UnixFromLine = newstr(munchstring(val, NULL, '\0'));
+ break;
+
+ case O_OPCHARS: /* operator characters (old $o macro) */
+ OperatorChars = newstr(munchstring(val, NULL, '\0'));
+ break;
+
+ case O_DONTINITGRPS: /* don't call initgroups(3) */
+ DontInitGroups = atobool(val);
+ break;
+
+ case O_SLFH: /* make sure from fits on one line */
+ SingleLineFromHeader = atobool(val);
+ break;
+
+ case O_ABH: /* allow HELO commands with syntax errors */
+ AllowBogusHELO = atobool(val);
+ break;
+
+ case O_CONNTHROT: /* connection rate throttle */
+ ConnRateThrottle = atoi(val);
+ break;
+
+ case O_UGW: /* group writable files are unsafe */
+ if (!atobool(val))
+ DontBlameSendmail |= DBS_GROUPWRITABLEFORWARDFILESAFE|DBS_GROUPWRITABLEINCLUDEFILESAFE;
+ break;
+
+ case O_DBLBOUNCE: /* address to which to send double bounces */
+ if (val[0] != '\0')
+ DoubleBounceAddr = newstr(val);
+ else
+ syserr("readcf: option DoubleBounceAddress: value required");
+ break;
+
+ case O_HSDIR: /* persistent host status directory */
+ if (val[0] != '\0')
+ HostStatDir = newstr(val);
+ break;
+
+ case O_SINGTHREAD: /* single thread deliveries (requires hsdir) */
+ SingleThreadDelivery = atobool(val);
+ break;
+
+ case O_RUNASUSER: /* run bulk of code as this user */
+ for (p = val; *p != '\0'; p++)
+ {
+ if (*p == '.' || *p == '/' || *p == ':')
+ {
+ *p++ = '\0';
+ break;
+ }
+ }
+ if (isascii(*val) && isdigit(*val))
+ {
+ if (can_setuid)
+ RunAsUid = atoi(val);
+ }
+ else
+ {
+ register struct passwd *pw;
+
+ pw = sm_getpwnam(val);
+ if (pw == NULL)
+ syserr("readcf: option RunAsUser: unknown user %s", val);
+ else if (can_setuid)
+ {
+ if (*p == '\0')
+ RunAsUserName = newstr(val);
+ RunAsUid = pw->pw_uid;
+ RunAsGid = pw->pw_gid;
+ }
+ }
+#ifdef UID_MAX
+ if (RunAsUid > UID_MAX)
+ {
+ syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
+ RunAsUid, UID_MAX);
+ }
+#endif
+ if (*p != '\0')
+ {
+ if (isascii(*p) && isdigit(*p))
+ {
+ if (can_setuid)
+ RunAsGid = atoi(p);
+ }
+ else
+ {
+ register struct group *gr;
+
+ gr = getgrnam(p);
+ if (gr == NULL)
+ syserr("readcf: option RunAsUser: unknown group %s",
+ p);
+ else if (can_setuid)
+ RunAsGid = gr->gr_gid;
+ }
+ }
+ if (tTd(47, 5))
+ printf("readcf: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
+ break;
+
+#if _FFR_DSN_RRT_OPTION
+ case O_DSN_RRT:
+ RrtImpliesDsn = atobool(val);
+ break;
+#endif
+
+#if _FFR_PIDFILE_OPTION
+ case O_PIDFILE:
+ free(PidFile);
+ PidFile = newstr(val);
+ break;
+#endif
+
+ case O_DONTBLAMESENDMAIL:
+ p = val;
+ for (;;)
+ {
+ register struct dbsval *dbs;
+ extern struct dbsval DontBlameSendmailValues[];
+
+ while (isascii(*p) && (isspace(*p) || ispunct(*p)))
+ p++;
+ if (*p == '\0')
+ break;
+ val = p;
+ while (isascii(*p) && isalnum(*p))
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+
+ for (dbs = DontBlameSendmailValues;
+ dbs->dbs_name != NULL; dbs++)
+ {
+ if (strcasecmp(val, dbs->dbs_name) == 0)
+ break;
+ }
+ if (dbs->dbs_name == NULL)
+ syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
+ else if (dbs->dbs_flag == DBS_SAFE)
+ DontBlameSendmail = DBS_SAFE;
+ else
+ DontBlameSendmail |= dbs->dbs_flag;
+ }
+ sticky = FALSE;
+ break;
+
+ case O_DPI:
+ DontProbeInterfaces = atobool(val);
+ break;
+
+ case O_MAXRCPT:
+ MaxRcptPerMsg = atoi(val);
+ break;
+
+#if _FFR_DEADLETTERDROP_OPTION
+ case O_DEADLETTER:
+ if (DeadLetterDrop != NULL)
+ free(DeadLetterDrop);
+ DeadLetterDrop = newstr(val);
+ break;
+#endif
+
+#if _FFR_DONTLOCKFILESFORREAD_OPTION
+ case O_DONTLOCK:
+ DontLockReadFiles = atobool(val);
+ break;
+#endif
+
+#if _FFR_MAXALIASRECURSION_OPTION
+ case O_MAXALIASRCSN:
+ MaxAliasRecursion = atoi(val);
+ break;
+#endif
+
+#if _FFR_CONNECTONLYTO_OPTION
+ case O_CNCTONLYTO:
+ /* XXX should probably use gethostbyname */
+ ConnectOnlyTo = inet_addr(val);
+ break;
+#endif
+
+#if _FFR_TRUSTED_FILE_OWNER
+ case O_TRUSTFILEOWN:
+ if (isascii(*val) && isdigit(*val))
+ TrustedFileUid = atoi(val);
+ else
+ {
+ register struct passwd *pw;
+
+ TrustedFileUid = 0;
+ pw = sm_getpwnam(val);
+ if (pw == NULL)
+ syserr("readcf: option TrustedFileOwner: unknown user %s", val);
+ else
+ TrustedFileUid = pw->pw_uid;
+ }
+
+#ifdef UID_MAX
+ if (TrustedFileUid > UID_MAX)
+ {
+ syserr("readcf: option TrustedFileOwner: uid value (%ld) > UID_MAX (%ld)",
+ TrustedFileUid, UID_MAX);
+ TrustedFileUid = 0;
+ }
+#endif
+ break;
+#endif
+
+ default:
+ if (tTd(37, 1))
+ {
+ if (isascii(opt) && isprint(opt))
+ printf("Warning: option %c unknown\n", opt);
+ else
+ printf("Warning: option 0x%x unknown\n", opt);
+ }
+ break;
+ }
+ if (sticky)
+ setbitn(opt, StickyOpt);
+}
+ /*
+** SETCLASS -- set a string into a class
+**
+** Parameters:
+** class -- the class to put the string in.
+** str -- the string to enter
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** puts the word into the symbol table.
+*/
+
+void
+setclass(class, str)
+ int class;
+ char *str;
+{
+ register STAB *s;
+
+ if (tTd(37, 8))
+ printf("setclass(%s, %s)\n", macname(class), str);
+ s = stab(str, ST_CLASS, ST_ENTER);
+ setbitn(class, s->s_class);
+}
+ /*
+** MAKEMAPENTRY -- create a map entry
+**
+** Parameters:
+** line -- the config file line
+**
+** Returns:
+** A pointer to the map that has been created.
+** NULL if there was a syntax error.
+**
+** Side Effects:
+** Enters the map into the dictionary.
+*/
+
+MAP *
+makemapentry(line)
+ char *line;
+{
+ register char *p;
+ char *mapname;
+ char *classname;
+ register STAB *s;
+ STAB *class;
+
+ for (p = line; isascii(*p) && isspace(*p); p++)
+ continue;
+ if (!(isascii(*p) && isalnum(*p)))
+ {
+ syserr("readcf: config K line: no map name");
+ return NULL;
+ }
+
+ mapname = p;
+ while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
+ continue;
+ if (*p != '\0')
+ *p++ = '\0';
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (!(isascii(*p) && isalnum(*p)))
+ {
+ syserr("readcf: config K line, map %s: no map class", mapname);
+ return NULL;
+ }
+ classname = p;
+ while (isascii(*++p) && isalnum(*p))
+ continue;
+ if (*p != '\0')
+ *p++ = '\0';
+ while (isascii(*p) && isspace(*p))
+ p++;
+
+ /* look up the class */
+ class = stab(classname, ST_MAPCLASS, ST_FIND);
+ if (class == NULL)
+ {
+ syserr("readcf: map %s: class %s not available", mapname, classname);
+ return NULL;
+ }
+
+ /* enter the map */
+ s = stab(mapname, ST_MAP, ST_ENTER);
+ s->s_map.map_class = &class->s_mapclass;
+ s->s_map.map_mname = newstr(mapname);
+
+ if (class->s_mapclass.map_parse(&s->s_map, p))
+ s->s_map.map_mflags |= MF_VALID;
+
+ if (tTd(37, 5))
+ {
+ printf("map %s, class %s, flags %lx, file %s,\n",
+ s->s_map.map_mname, s->s_map.map_class->map_cname,
+ s->s_map.map_mflags,
+ s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
+ printf("\tapp %s, domain %s, rebuild %s\n",
+ s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
+ s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
+ s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
+ }
+
+ return &s->s_map;
+}
+ /*
+** STRTORWSET -- convert string to rewriting set number
+**
+** Parameters:
+** p -- the pointer to the string to decode.
+** endp -- if set, store the trailing delimiter here.
+** stabmode -- ST_ENTER to create this entry, ST_FIND if
+** it must already exist.
+**
+** Returns:
+** The appropriate ruleset number.
+** -1 if it is not valid (error already printed)
+*/
+
+int
+strtorwset(p, endp, stabmode)
+ char *p;
+ char **endp;
+ int stabmode;
+{
+ int ruleset;
+ static int nextruleset = MAXRWSETS;
+
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (!isascii(*p))
+ {
+ syserr("invalid ruleset name: \"%.20s\"", p);
+ return -1;
+ }
+ if (isdigit(*p))
+ {
+ ruleset = strtol(p, endp, 10);
+ if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
+ {
+ syserr("bad ruleset %d (%d max)",
+ ruleset, MAXRWSETS / 2);
+ ruleset = -1;
+ }
+ }
+ else
+ {
+ STAB *s;
+ char delim;
+ char *q;
+
+ q = p;
+ while (*p != '\0' && isascii(*p) &&
+ (isalnum(*p) || *p == '_'))
+ p++;
+ if (q == p || !(isascii(*q) && isalpha(*q)))
+ {
+ /* no valid characters */
+ syserr("invalid ruleset name: \"%.20s\"", q);
+ return -1;
+ }
+ while (isascii(*p) && isspace(*p))
+ *p++ = '\0';
+ delim = *p;
+ if (delim != '\0')
+ *p = '\0';
+ s = stab(q, ST_RULESET, stabmode);
+ if (delim != '\0')
+ *p = delim;
+
+ if (s == NULL)
+ return -1;
+
+ if (stabmode == ST_ENTER && delim == '=')
+ {
+ while (isascii(*++p) && isspace(*p))
+ continue;
+ if (!(isascii(*p) && isdigit(*p)))
+ {
+ syserr("bad ruleset definition \"%s\" (number required after `=')", q);
+ ruleset = -1;
+ }
+ else
+ {
+ ruleset = strtol(p, endp, 10);
+ if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
+ {
+ syserr("bad ruleset number %d in \"%s\" (%d max)",
+ ruleset, q, MAXRWSETS / 2);
+ ruleset = -1;
+ }
+ }
+ }
+ else
+ {
+ if (endp != NULL)
+ *endp = p;
+ if (s->s_ruleset > 0)
+ ruleset = s->s_ruleset;
+ else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
+ {
+ syserr("%s: too many named rulesets (%d max)",
+ q, MAXRWSETS / 2);
+ ruleset = -1;
+ }
+ }
+ if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
+ {
+ syserr("%s: ruleset changed value (old %d, new %d)",
+ q, s->s_ruleset, ruleset);
+ ruleset = s->s_ruleset;
+ }
+ else if (ruleset > 0)
+ {
+ s->s_ruleset = ruleset;
+ }
+ }
+ return ruleset;
+}
+ /*
+** INITTIMEOUTS -- parse and set timeout values
+**
+** Parameters:
+** val -- a pointer to the values. If NULL, do initial
+** settings.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Initializes the TimeOuts structure
+*/
+
+#define SECONDS
+#define MINUTES * 60
+#define HOUR * 3600
+
+void
+inittimeouts(val)
+ register char *val;
+{
+ register char *p;
+ extern time_t convtime __P((char *, char));
+
+ if (tTd(37, 2))
+ printf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
+ if (val == NULL)
+ {
+ TimeOuts.to_connect = (time_t) 0 SECONDS;
+ TimeOuts.to_initial = (time_t) 5 MINUTES;
+ TimeOuts.to_helo = (time_t) 5 MINUTES;
+ TimeOuts.to_mail = (time_t) 10 MINUTES;
+ TimeOuts.to_rcpt = (time_t) 1 HOUR;
+ TimeOuts.to_datainit = (time_t) 5 MINUTES;
+ TimeOuts.to_datablock = (time_t) 1 HOUR;
+ TimeOuts.to_datafinal = (time_t) 1 HOUR;
+ TimeOuts.to_rset = (time_t) 5 MINUTES;
+ TimeOuts.to_quit = (time_t) 2 MINUTES;
+ TimeOuts.to_nextcommand = (time_t) 1 HOUR;
+ TimeOuts.to_miscshort = (time_t) 2 MINUTES;
+#if IDENTPROTO
+ TimeOuts.to_ident = (time_t) 30 SECONDS;
+#else
+ TimeOuts.to_ident = (time_t) 0 SECONDS;
+#endif
+ TimeOuts.to_fileopen = (time_t) 60 SECONDS;
+ if (tTd(37, 5))
+ {
+ printf("Timeouts:\n");
+ printf(" connect = %ld\n", (long)TimeOuts.to_connect);
+ printf(" initial = %ld\n", (long)TimeOuts.to_initial);
+ printf(" helo = %ld\n", (long)TimeOuts.to_helo);
+ printf(" mail = %ld\n", (long)TimeOuts.to_mail);
+ printf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt);
+ printf(" datainit = %ld\n", (long)TimeOuts.to_datainit);
+ printf(" datablock = %ld\n", (long)TimeOuts.to_datablock);
+ printf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal);
+ printf(" rset = %ld\n", (long)TimeOuts.to_rset);
+ printf(" quit = %ld\n", (long)TimeOuts.to_quit);
+ printf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand);
+ printf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort);
+ printf(" ident = %ld\n", (long)TimeOuts.to_ident);
+ printf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen);
+ }
+ return;
+ }
+
+ for (;; val = p)
+ {
+ while (isascii(*val) && isspace(*val))
+ val++;
+ if (*val == '\0')
+ break;
+ for (p = val; *p != '\0' && *p != ','; p++)
+ continue;
+ if (*p != '\0')
+ *p++ = '\0';
+
+ if (isascii(*val) && isdigit(*val))
+ {
+ /* old syntax -- set everything */
+ TimeOuts.to_mail = convtime(val, 'm');
+ TimeOuts.to_rcpt = TimeOuts.to_mail;
+ TimeOuts.to_datainit = TimeOuts.to_mail;
+ TimeOuts.to_datablock = TimeOuts.to_mail;
+ TimeOuts.to_datafinal = TimeOuts.to_mail;
+ TimeOuts.to_nextcommand = TimeOuts.to_mail;
+ continue;
+ }
+ else
+ {
+ register char *q = strchr(val, ':');
+
+ if (q == NULL && (q = strchr(val, '=')) == NULL)
+ {
+ /* syntax error */
+ continue;
+ }
+ *q++ = '\0';
+ settimeout(val, q);
+ }
+ }
+}
+ /*
+** SETTIMEOUT -- set an individual timeout
+**
+** Parameters:
+** name -- the name of the timeout.
+** val -- the value of the timeout.
+**
+** Returns:
+** none.
+*/
+
+void
+settimeout(name, val)
+ char *name;
+ char *val;
+{
+ register char *p;
+ time_t to;
+ extern time_t convtime __P((char *, char));
+
+ if (tTd(37, 2))
+ printf("settimeout(%s = %s)\n", name, val);
+
+ to = convtime(val, 'm');
+ p = strchr(name, '.');
+ if (p != NULL)
+ *p++ = '\0';
+
+ if (strcasecmp(name, "initial") == 0)
+ TimeOuts.to_initial = to;
+ else if (strcasecmp(name, "mail") == 0)
+ TimeOuts.to_mail = to;
+ else if (strcasecmp(name, "rcpt") == 0)
+ TimeOuts.to_rcpt = to;
+ else if (strcasecmp(name, "datainit") == 0)
+ TimeOuts.to_datainit = to;
+ else if (strcasecmp(name, "datablock") == 0)
+ TimeOuts.to_datablock = to;
+ else if (strcasecmp(name, "datafinal") == 0)
+ TimeOuts.to_datafinal = to;
+ else if (strcasecmp(name, "command") == 0)
+ TimeOuts.to_nextcommand = to;
+ else if (strcasecmp(name, "rset") == 0)
+ TimeOuts.to_rset = to;
+ else if (strcasecmp(name, "helo") == 0)
+ TimeOuts.to_helo = to;
+ else if (strcasecmp(name, "quit") == 0)
+ TimeOuts.to_quit = to;
+ else if (strcasecmp(name, "misc") == 0)
+ TimeOuts.to_miscshort = to;
+ else if (strcasecmp(name, "ident") == 0)
+ TimeOuts.to_ident = to;
+ else if (strcasecmp(name, "fileopen") == 0)
+ TimeOuts.to_fileopen = to;
+ else if (strcasecmp(name, "connect") == 0)
+ TimeOuts.to_connect = to;
+ else if (strcasecmp(name, "iconnect") == 0)
+ TimeOuts.to_iconnect = to;
+ else if (strcasecmp(name, "queuewarn") == 0)
+ {
+ to = convtime(val, 'h');
+ if (p == NULL || strcmp(p, "*") == 0)
+ {
+ TimeOuts.to_q_warning[TOC_NORMAL] = to;
+ TimeOuts.to_q_warning[TOC_URGENT] = to;
+ TimeOuts.to_q_warning[TOC_NONURGENT] = to;
+ }
+ else if (strcasecmp(p, "normal") == 0)
+ TimeOuts.to_q_warning[TOC_NORMAL] = to;
+ else if (strcasecmp(p, "urgent") == 0)
+ TimeOuts.to_q_warning[TOC_URGENT] = to;
+ else if (strcasecmp(p, "non-urgent") == 0)
+ TimeOuts.to_q_warning[TOC_NONURGENT] = to;
+ else
+ syserr("settimeout: invalid queuewarn subtimeout %s", p);
+ }
+ else if (strcasecmp(name, "queuereturn") == 0)
+ {
+ to = convtime(val, 'd');
+ if (p == NULL || strcmp(p, "*") == 0)
+ {
+ TimeOuts.to_q_return[TOC_NORMAL] = to;
+ TimeOuts.to_q_return[TOC_URGENT] = to;
+ TimeOuts.to_q_return[TOC_NONURGENT] = to;
+ }
+ else if (strcasecmp(p, "normal") == 0)
+ TimeOuts.to_q_return[TOC_NORMAL] = to;
+ else if (strcasecmp(p, "urgent") == 0)
+ TimeOuts.to_q_return[TOC_URGENT] = to;
+ else if (strcasecmp(p, "non-urgent") == 0)
+ TimeOuts.to_q_return[TOC_NONURGENT] = to;
+ else
+ syserr("settimeout: invalid queuereturn subtimeout %s", p);
+ }
+ else if (strcasecmp(name, "hoststatus") == 0)
+ MciInfoTimeout = convtime(val, 'm');
+ else
+ syserr("settimeout: invalid timeout %s", name);
+}
diff --git a/contrib/sendmail/src/recipient.c b/contrib/sendmail/src/recipient.c
new file mode 100644
index 0000000..9a73b6a
--- /dev/null
+++ b/contrib/sendmail/src/recipient.c
@@ -0,0 +1,1431 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)recipient.c 8.154 (Berkeley) 6/24/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include <grp.h>
+
+/*
+** SENDTOLIST -- Designate a send list.
+**
+** The parameter is a comma-separated list of people to send to.
+** This routine arranges to send to all of them.
+**
+** Parameters:
+** list -- the send list.
+** ctladdr -- the address template for the person to
+** send to -- effective uid/gid are important.
+** This is typically the alias that caused this
+** expansion.
+** sendq -- a pointer to the head of a queue to put
+** these people into.
+** aliaslevel -- the current alias nesting depth -- to
+** diagnose loops.
+** e -- the envelope in which to add these recipients.
+**
+** Returns:
+** The number of addresses actually on the list.
+**
+** Side Effects:
+** none.
+*/
+
+/* q_flags bits inherited from ctladdr */
+#define QINHERITEDBITS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY)
+
+int
+sendtolist(list, ctladdr, sendq, aliaslevel, e)
+ char *list;
+ ADDRESS *ctladdr;
+ ADDRESS **sendq;
+ int aliaslevel;
+ register ENVELOPE *e;
+{
+ register char *p;
+ register ADDRESS *al; /* list of addresses to send to */
+ char delimiter; /* the address delimiter */
+ int naddrs;
+ int i;
+ char *oldto = e->e_to;
+ char *bufp;
+ char buf[MAXNAME + 1];
+
+ if (list == NULL)
+ {
+ syserr("sendtolist: null list");
+ return 0;
+ }
+
+ if (tTd(25, 1))
+ {
+ printf("sendto: %s\n ctladdr=", list);
+ printaddr(ctladdr, FALSE);
+ }
+
+ /* heuristic to determine old versus new style addresses */
+ if (ctladdr == NULL &&
+ (strchr(list, ',') != NULL || strchr(list, ';') != NULL ||
+ strchr(list, '<') != NULL || strchr(list, '(') != NULL))
+ e->e_flags &= ~EF_OLDSTYLE;
+ delimiter = ' ';
+ if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL)
+ delimiter = ',';
+
+ al = NULL;
+ naddrs = 0;
+
+ /* make sure we have enough space to copy the string */
+ i = strlen(list) + 1;
+ if (i <= sizeof buf)
+ bufp = buf;
+ else
+ bufp = xalloc(i);
+ strcpy(bufp, denlstring(list, FALSE, TRUE));
+
+ for (p = bufp; *p != '\0'; )
+ {
+ auto char *delimptr;
+ register ADDRESS *a;
+
+ /* parse the address */
+ while ((isascii(*p) && isspace(*p)) || *p == ',')
+ p++;
+ a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e);
+ p = delimptr;
+ if (a == NULL)
+ continue;
+ a->q_next = al;
+ a->q_alias = ctladdr;
+
+ /* arrange to inherit attributes from parent */
+ if (ctladdr != NULL)
+ {
+ ADDRESS *b;
+ extern ADDRESS *self_reference __P((ADDRESS *, ENVELOPE *));
+
+ /* self reference test */
+ if (sameaddr(ctladdr, a))
+ {
+ if (tTd(27, 5))
+ {
+ printf("sendtolist: QSELFREF ");
+ printaddr(ctladdr, FALSE);
+ }
+ ctladdr->q_flags |= QSELFREF;
+ }
+
+ /* check for address loops */
+ b = self_reference(a, e);
+ if (b != NULL)
+ {
+ b->q_flags |= QSELFREF;
+ if (tTd(27, 5))
+ {
+ printf("sendtolist: QSELFREF ");
+ printaddr(b, FALSE);
+ }
+ if (a != b)
+ {
+ if (tTd(27, 5))
+ {
+ printf("sendtolist: QDONTSEND ");
+ printaddr(a, FALSE);
+ }
+ a->q_flags |= QDONTSEND;
+ b->q_flags |= a->q_flags & QNOTREMOTE;
+ continue;
+ }
+ }
+
+ /* full name */
+ if (a->q_fullname == NULL)
+ a->q_fullname = ctladdr->q_fullname;
+
+ /* various flag bits */
+ a->q_flags &= ~QINHERITEDBITS;
+ a->q_flags |= ctladdr->q_flags & QINHERITEDBITS;
+
+ /* original recipient information */
+ a->q_orcpt = ctladdr->q_orcpt;
+ }
+
+ al = a;
+ }
+
+ /* arrange to send to everyone on the local send list */
+ while (al != NULL)
+ {
+ register ADDRESS *a = al;
+
+ al = a->q_next;
+ a = recipient(a, sendq, aliaslevel, e);
+ naddrs++;
+ }
+
+ e->e_to = oldto;
+ if (bufp != buf)
+ free(bufp);
+ return (naddrs);
+}
+ /*
+** RECIPIENT -- Designate a message recipient
+**
+** Saves the named person for future mailing.
+**
+** Parameters:
+** a -- the (preparsed) address header for the recipient.
+** sendq -- a pointer to the head of a queue to put the
+** recipient in. Duplicate supression is done
+** in this queue.
+** aliaslevel -- the current alias nesting depth.
+** e -- the current envelope.
+**
+** Returns:
+** The actual address in the queue. This will be "a" if
+** the address is not a duplicate, else the original address.
+**
+** Side Effects:
+** none.
+*/
+
+ADDRESS *
+recipient(a, sendq, aliaslevel, e)
+ register ADDRESS *a;
+ register ADDRESS **sendq;
+ int aliaslevel;
+ register ENVELOPE *e;
+{
+ register ADDRESS *q;
+ ADDRESS **pq;
+ register struct mailer *m;
+ register char *p;
+ bool quoted = FALSE; /* set if the addr has a quote bit */
+ int findusercount = 0;
+ bool initialdontsend = bitset(QDONTSEND, a->q_flags);
+ int i;
+ char *buf;
+ char buf0[MAXNAME + 1]; /* unquoted image of the user name */
+ extern void alias __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
+
+ e->e_to = a->q_paddr;
+ m = a->q_mailer;
+ errno = 0;
+ if (aliaslevel == 0)
+ a->q_flags |= QPRIMARY;
+ if (tTd(26, 1))
+ {
+ printf("\nrecipient (%d): ", aliaslevel);
+ printaddr(a, FALSE);
+ }
+
+ /* if this is primary, add it to the original recipient list */
+ if (a->q_alias == NULL)
+ {
+ if (e->e_origrcpt == NULL)
+ e->e_origrcpt = a->q_paddr;
+ else if (e->e_origrcpt != a->q_paddr)
+ e->e_origrcpt = "";
+ }
+
+ /* break aliasing loops */
+ if (aliaslevel > MaxAliasRecursion)
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.4.6";
+ usrerr("554 aliasing/forwarding loop broken (%d aliases deep; %d max)",
+ aliaslevel, MaxAliasRecursion);
+ return (a);
+ }
+
+ /*
+ ** Finish setting up address structure.
+ */
+
+ /* get unquoted user for file, program or user.name check */
+ i = strlen(a->q_user);
+ if (i >= sizeof buf0)
+ buf = xalloc(i + 1);
+ else
+ buf = buf0;
+ (void) strcpy(buf, a->q_user);
+ for (p = buf; *p != '\0' && !quoted; p++)
+ {
+ if (*p == '\\')
+ quoted = TRUE;
+ }
+ stripquotes(buf);
+
+ /* check for direct mailing to restricted mailers */
+ if (m == ProgMailer)
+ {
+ if (a->q_alias == NULL)
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.7.1";
+ usrerr("550 Cannot mail directly to programs");
+ }
+ else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.7.1";
+ if (a->q_alias->q_ruser == NULL)
+ usrerr("550 UID %d is an unknown user: cannot mail to programs",
+ a->q_alias->q_uid);
+ else
+ usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs",
+ a->q_alias->q_ruser, MyHostName);
+ }
+ else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.7.1";
+ a->q_rstatus = newstr("Unsafe for mailing to programs");
+ usrerr("550 Address %s is unsafe for mailing to programs",
+ a->q_alias->q_paddr);
+ }
+ }
+
+ /*
+ ** Look up this person in the recipient list.
+ ** If they are there already, return, otherwise continue.
+ ** If the list is empty, just add it. Notice the cute
+ ** hack to make from addresses suppress things correctly:
+ ** the QDONTSEND bit will be set in the send list.
+ ** [Please note: the emphasis is on "hack."]
+ */
+
+ for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
+ {
+ if (sameaddr(q, a) &&
+ (bitset(QRCPTOK, q->q_flags) ||
+ !bitset(QPRIMARY, q->q_flags)))
+ {
+ if (tTd(26, 1))
+ {
+ printf("%s in sendq: ", a->q_paddr);
+ printaddr(q, FALSE);
+ }
+ if (!bitset(QPRIMARY, q->q_flags))
+ {
+ if (!bitset(QDONTSEND, a->q_flags))
+ message("duplicate suppressed");
+ q->q_flags |= a->q_flags;
+ }
+ else if (bitset(QSELFREF, q->q_flags))
+ q->q_flags |= a->q_flags & ~QDONTSEND;
+ a = q;
+ goto done;
+ }
+ }
+
+ /* add address on list */
+ *pq = a;
+ a->q_next = NULL;
+
+ /*
+ ** Alias the name and handle special mailer types.
+ */
+
+ trylocaluser:
+ if (tTd(29, 7))
+ printf("at trylocaluser %s\n", a->q_user);
+
+ if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
+ goto testselfdestruct;
+
+ if (m == InclMailer)
+ {
+ a->q_flags |= QDONTSEND;
+ if (a->q_alias == NULL)
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.7.1";
+ usrerr("550 Cannot mail directly to :include:s");
+ }
+ else
+ {
+ int ret;
+
+ message("including file %s", a->q_user);
+ ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e);
+ if (transienterror(ret))
+ {
+ if (LogLevel > 2)
+ sm_syslog(LOG_ERR, e->e_id,
+ "include %s: transient error: %s",
+ shortenstring(a->q_user, MAXSHORTSTR),
+ errstring(ret));
+ a->q_flags |= QQUEUEUP;
+ a->q_flags &= ~QDONTSEND;
+ usrerr("451 Cannot open %s: %s",
+ shortenstring(a->q_user, MAXSHORTSTR),
+ errstring(ret));
+ }
+ else if (ret != 0)
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.2.4";
+ usrerr("550 Cannot open %s: %s",
+ shortenstring(a->q_user, MAXSHORTSTR),
+ errstring(ret));
+ }
+ }
+ }
+ else if (m == FileMailer)
+ {
+ extern bool writable __P((char *, ADDRESS *, int));
+
+ /* check if writable or creatable */
+ if (a->q_alias == NULL)
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.7.1";
+ usrerr("550 Cannot mail directly to files");
+ }
+ else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.7.1";
+ if (a->q_alias->q_ruser == NULL)
+ usrerr("550 UID %d is an unknown user: cannot mail to files",
+ a->q_alias->q_uid);
+ else
+ usrerr("550 User %s@%s doesn't have a valid shell for mailing to files",
+ a->q_alias->q_ruser, MyHostName);
+ }
+ else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.7.1";
+ a->q_rstatus = newstr("Unsafe for mailing to files");
+ usrerr("550 Address %s is unsafe for mailing to files",
+ a->q_alias->q_paddr);
+ }
+ else if (strcmp(buf, "/dev/null") == 0)
+ {
+ /* /dev/null is always accepted */
+ }
+ else if (!writable(buf, a->q_alias, SFF_CREAT))
+ {
+ a->q_flags |= QBADADDR;
+ giveresponse(EX_CANTCREAT, m, NULL, a->q_alias,
+ (time_t) 0, e);
+ }
+ }
+
+ /* try aliasing */
+ if (!quoted && !bitset(QDONTSEND, a->q_flags) &&
+ bitnset(M_ALIASABLE, m->m_flags))
+ alias(a, sendq, aliaslevel, e);
+
+# if USERDB
+ /* if not aliased, look it up in the user database */
+ if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags) &&
+ bitnset(M_CHECKUDB, m->m_flags))
+ {
+ extern int udbexpand __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
+
+ if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL)
+ {
+ a->q_flags |= QQUEUEUP;
+ if (e->e_message == NULL)
+ e->e_message = newstr("Deferred: user database error");
+ if (LogLevel > 8)
+ sm_syslog(LOG_INFO, e->e_id,
+ "deferred: udbexpand: %s",
+ errstring(errno));
+ message("queued (user database error): %s",
+ errstring(errno));
+ e->e_nrcpts++;
+ goto testselfdestruct;
+ }
+ }
+# endif
+
+ /*
+ ** If we have a level two config file, then pass the name through
+ ** Ruleset 5 before sending it off. Ruleset 5 has the right
+ ** to send rewrite it to another mailer. This gives us a hook
+ ** after local aliasing has been done.
+ */
+
+ if (tTd(29, 5))
+ {
+ printf("recipient: testing local? cl=%d, rr5=%lx\n\t",
+ ConfigLevel, (u_long) RewriteRules[5]);
+ printaddr(a, FALSE);
+ }
+ if (!bitset(QNOTREMOTE|QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
+ ConfigLevel >= 2 && RewriteRules[5] != NULL &&
+ bitnset(M_TRYRULESET5, m->m_flags))
+ {
+ extern void maplocaluser __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
+
+ maplocaluser(a, sendq, aliaslevel + 1, e);
+ }
+
+ /*
+ ** If it didn't get rewritten to another mailer, go ahead
+ ** and deliver it.
+ */
+
+ if (!bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
+ bitnset(M_HASPWENT, m->m_flags))
+ {
+ auto bool fuzzy;
+ register struct passwd *pw;
+ extern void forward __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
+
+ /* warning -- finduser may trash buf */
+ pw = finduser(buf, &fuzzy);
+ if (pw == NULL || strlen(pw->pw_name) > MAXNAME)
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.1.1";
+ giveresponse(EX_NOUSER, m, NULL, a->q_alias,
+ (time_t) 0, e);
+ }
+ else
+ {
+ char nbuf[MAXNAME + 1];
+
+ if (fuzzy)
+ {
+ /* name was a fuzzy match */
+ a->q_user = newstr(pw->pw_name);
+ if (findusercount++ > 3)
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.4.6";
+ usrerr("554 aliasing/forwarding loop for %s broken",
+ pw->pw_name);
+ goto done;
+ }
+
+ /* see if it aliases */
+ (void) strcpy(buf, pw->pw_name);
+ goto trylocaluser;
+ }
+ if (strcmp(pw->pw_dir, "/") == 0)
+ a->q_home = "";
+ else
+ a->q_home = newstr(pw->pw_dir);
+ a->q_uid = pw->pw_uid;
+ a->q_gid = pw->pw_gid;
+ a->q_ruser = newstr(pw->pw_name);
+ a->q_flags |= QGOODUID;
+ buildfname(pw->pw_gecos, pw->pw_name, nbuf, sizeof nbuf);
+ if (nbuf[0] != '\0')
+ a->q_fullname = newstr(nbuf);
+ if (!usershellok(pw->pw_name, pw->pw_shell))
+ {
+ a->q_flags |= QBOGUSSHELL;
+ }
+ if (bitset(EF_VRFYONLY, e->e_flags))
+ {
+ /* don't do any more now */
+ a->q_flags |= QVERIFIED;
+ }
+ else if (!quoted)
+ forward(a, sendq, aliaslevel, e);
+ }
+ }
+ if (!bitset(QDONTSEND, a->q_flags))
+ e->e_nrcpts++;
+
+ testselfdestruct:
+ a->q_flags |= QTHISPASS;
+ if (tTd(26, 8))
+ {
+ printf("testselfdestruct: ");
+ printaddr(a, FALSE);
+ if (tTd(26, 10))
+ {
+ printf("SENDQ:\n");
+ printaddr(*sendq, TRUE);
+ printf("----\n");
+ }
+ }
+ if (a->q_alias == NULL && a != &e->e_from &&
+ bitset(QDONTSEND, a->q_flags))
+ {
+ for (q = *sendq; q != NULL; q = q->q_next)
+ {
+ if (!bitset(QDONTSEND, q->q_flags))
+ break;
+ }
+ if (q == NULL)
+ {
+ a->q_flags |= QBADADDR;
+ a->q_status = "5.4.6";
+ usrerr("554 aliasing/forwarding loop broken");
+ }
+ }
+
+ done:
+ a->q_flags |= QTHISPASS;
+ if (buf != buf0)
+ free(buf);
+
+ /*
+ ** If we are at the top level, check to see if this has
+ ** expanded to exactly one address. If so, it can inherit
+ ** the primaryness of the address.
+ **
+ ** While we're at it, clear the QTHISPASS bits.
+ */
+
+ if (aliaslevel == 0)
+ {
+ int nrcpts = 0;
+ ADDRESS *only = NULL;
+
+ for (q = *sendq; q != NULL; q = q->q_next)
+ {
+ if (bitset(QTHISPASS, q->q_flags) &&
+ !bitset(QDONTSEND|QBADADDR, q->q_flags))
+ {
+ nrcpts++;
+ only = q;
+ }
+ q->q_flags &= ~QTHISPASS;
+ }
+ if (nrcpts == 1)
+ {
+ /* check to see if this actually got a new owner */
+ q = only;
+ while ((q = q->q_alias) != NULL)
+ {
+ if (q->q_owner != NULL)
+ break;
+ }
+ if (q == NULL)
+ only->q_flags |= QPRIMARY;
+ }
+ else if (!initialdontsend && nrcpts > 0)
+ {
+ /* arrange for return receipt */
+ e->e_flags |= EF_SENDRECEIPT;
+ a->q_flags |= QEXPANDED;
+ if (e->e_xfp != NULL && bitset(QPINGONSUCCESS, a->q_flags))
+ fprintf(e->e_xfp,
+ "%s... expanded to multiple addresses\n",
+ a->q_paddr);
+ }
+ }
+ a->q_flags |= QRCPTOK;
+ return (a);
+}
+ /*
+** FINDUSER -- find the password entry for a user.
+**
+** This looks a lot like getpwnam, except that it may want to
+** do some fancier pattern matching in /etc/passwd.
+**
+** This routine contains most of the time of many sendmail runs.
+** It deserves to be optimized.
+**
+** Parameters:
+** name -- the name to match against.
+** fuzzyp -- an outarg that is set to TRUE if this entry
+** was found using the fuzzy matching algorithm;
+** set to FALSE otherwise.
+**
+** Returns:
+** A pointer to a pw struct.
+** NULL if name is unknown or ambiguous.
+**
+** Side Effects:
+** may modify name.
+*/
+
+struct passwd *
+finduser(name, fuzzyp)
+ char *name;
+ bool *fuzzyp;
+{
+ register struct passwd *pw;
+ register char *p;
+ bool tryagain;
+
+ if (tTd(29, 4))
+ printf("finduser(%s): ", name);
+
+ *fuzzyp = FALSE;
+
+#ifdef HESIOD
+ /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
+ for (p = name; *p != '\0'; p++)
+ if (!isascii(*p) || !isdigit(*p))
+ break;
+ if (*p == '\0')
+ {
+ if (tTd(29, 4))
+ printf("failed (numeric input)\n");
+ return NULL;
+ }
+#endif
+
+ /* look up this login name using fast path */
+ if ((pw = sm_getpwnam(name)) != NULL)
+ {
+ if (tTd(29, 4))
+ printf("found (non-fuzzy)\n");
+ return (pw);
+ }
+
+ /* try mapping it to lower case */
+ tryagain = FALSE;
+ for (p = name; *p != '\0'; p++)
+ {
+ if (isascii(*p) && isupper(*p))
+ {
+ *p = tolower(*p);
+ tryagain = TRUE;
+ }
+ }
+ if (tryagain && (pw = sm_getpwnam(name)) != NULL)
+ {
+ if (tTd(29, 4))
+ printf("found (lower case)\n");
+ *fuzzyp = TRUE;
+ return pw;
+ }
+
+#if MATCHGECOS
+ /* see if fuzzy matching allowed */
+ if (!MatchGecos)
+ {
+ if (tTd(29, 4))
+ printf("not found (fuzzy disabled)\n");
+ return NULL;
+ }
+
+ /* search for a matching full name instead */
+ for (p = name; *p != '\0'; p++)
+ {
+ if (*p == (SpaceSub & 0177) || *p == '_')
+ *p = ' ';
+ }
+ (void) setpwent();
+ while ((pw = getpwent()) != NULL)
+ {
+ char buf[MAXNAME + 1];
+
+# if 0
+ if (strcasecmp(pw->pw_name, name) == 0)
+ {
+ if (tTd(29, 4))
+ printf("found (case wrapped)\n");
+ break;
+ }
+# endif
+
+ buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf);
+ if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name))
+ {
+ if (tTd(29, 4))
+ printf("fuzzy matches %s\n", pw->pw_name);
+ message("sending to login name %s", pw->pw_name);
+ break;
+ }
+ }
+ if (pw != NULL)
+ *fuzzyp = TRUE;
+ else if (tTd(29, 4))
+ printf("no fuzzy match found\n");
+# if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */
+ endpwent();
+# endif
+ return pw;
+#else
+ if (tTd(29, 4))
+ printf("not found (fuzzy disabled)\n");
+ return NULL;
+#endif
+}
+ /*
+** WRITABLE -- predicate returning if the file is writable.
+**
+** This routine must duplicate the algorithm in sys/fio.c.
+** Unfortunately, we cannot use the access call since we
+** won't necessarily be the real uid when we try to
+** actually open the file.
+**
+** Notice that ANY file with ANY execute bit is automatically
+** not writable. This is also enforced by mailfile.
+**
+** Parameters:
+** filename -- the file name to check.
+** ctladdr -- the controlling address for this file.
+** flags -- SFF_* flags to control the function.
+**
+** Returns:
+** TRUE -- if we will be able to write this file.
+** FALSE -- if we cannot write this file.
+**
+** Side Effects:
+** none.
+*/
+
+bool
+writable(filename, ctladdr, flags)
+ char *filename;
+ ADDRESS *ctladdr;
+ int flags;
+{
+ uid_t euid;
+ gid_t egid;
+ char *uname;
+
+ if (tTd(44, 5))
+ printf("writable(%s, 0x%x)\n", filename, flags);
+
+ /*
+ ** File does exist -- check that it is writable.
+ */
+
+ if (geteuid() != 0)
+ {
+ euid = geteuid();
+ egid = getegid();
+ uname = NULL;
+ }
+ else if (ctladdr != NULL)
+ {
+ euid = ctladdr->q_uid;
+ egid = ctladdr->q_gid;
+ uname = ctladdr->q_user;
+ }
+ else if (bitset(SFF_RUNASREALUID, flags))
+ {
+ euid = RealUid;
+ egid = RealGid;
+ uname = RealUserName;
+ }
+ else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags))
+ {
+ euid = FileMailer->m_uid;
+ egid = FileMailer->m_gid;
+ uname = NULL;
+ }
+ else
+ {
+ euid = egid = 0;
+ uname = NULL;
+ }
+ if (!bitset(SFF_ROOTOK, flags))
+ {
+ if (euid == 0)
+ {
+ euid = DefUid;
+ uname = DefUser;
+ }
+ if (egid == 0)
+ egid = DefGid;
+ }
+ if (geteuid() == 0 &&
+ (ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags)))
+ flags |= SFF_SETUIDOK;
+
+ if (!bitset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
+ flags |= SFF_NOSLINK;
+ if (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
+ flags |= SFF_NOHLINK;
+
+ errno = safefile(filename, euid, egid, uname, flags, S_IWRITE, NULL);
+ return errno == 0;
+}
+ /*
+** INCLUDE -- handle :include: specification.
+**
+** Parameters:
+** fname -- filename to include.
+** forwarding -- if TRUE, we are reading a .forward file.
+** if FALSE, it's a :include: file.
+** ctladdr -- address template to use to fill in these
+** addresses -- effective user/group id are
+** the important things.
+** sendq -- a pointer to the head of the send queue
+** to put these addresses in.
+** aliaslevel -- the alias nesting depth.
+** e -- the current envelope.
+**
+** Returns:
+** open error status
+**
+** Side Effects:
+** reads the :include: file and sends to everyone
+** listed in that file.
+**
+** Security Note:
+** If you have restricted chown (that is, you can't
+** give a file away), it is reasonable to allow programs
+** and files called from this :include: file to be to be
+** run as the owner of the :include: file. This is bogus
+** if there is any chance of someone giving away a file.
+** We assume that pre-POSIX systems can give away files.
+**
+** There is an additional restriction that if you
+** forward to a :include: file, it will not take on
+** the ownership of the :include: file. This may not
+** be necessary, but shouldn't hurt.
+*/
+
+static jmp_buf CtxIncludeTimeout;
+static void includetimeout __P((void));
+
+int
+include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
+ char *fname;
+ bool forwarding;
+ ADDRESS *ctladdr;
+ ADDRESS **sendq;
+ int aliaslevel;
+ ENVELOPE *e;
+{
+ FILE *volatile fp = NULL;
+ char *oldto = e->e_to;
+ char *oldfilename = FileName;
+ int oldlinenumber = LineNumber;
+ register EVENT *ev = NULL;
+ int nincludes;
+ int mode;
+ register ADDRESS *ca;
+ volatile uid_t saveduid, uid;
+ volatile gid_t savedgid, gid;
+ char *volatile uname;
+ int rval = 0;
+ volatile int sfflags = SFF_REGONLY;
+ register char *p;
+ bool safechown = FALSE;
+ volatile bool safedir = FALSE;
+ struct stat st;
+ char buf[MAXLINE];
+ extern bool chownsafe __P((int, bool));
+
+ if (tTd(27, 2))
+ printf("include(%s)\n", fname);
+ if (tTd(27, 4))
+ printf(" ruid=%d euid=%d\n", (int) getuid(), (int) geteuid());
+ if (tTd(27, 14))
+ {
+ printf("ctladdr ");
+ printaddr(ctladdr, FALSE);
+ }
+
+ if (tTd(27, 9))
+ printf("include: old uid = %d/%d\n",
+ (int) getuid(), (int) geteuid());
+
+ if (forwarding)
+ sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOSLINK;
+
+ ca = getctladdr(ctladdr);
+ if (ca == NULL)
+ {
+ uid = DefUid;
+ gid = DefGid;
+ uname = DefUser;
+ }
+ else
+ {
+ uid = ca->q_uid;
+ gid = ca->q_gid;
+ uname = ca->q_user;
+ }
+#if HASSETREUID || USESETEUID
+ saveduid = geteuid();
+ savedgid = getegid();
+ if (saveduid == 0)
+ {
+ if (!DontInitGroups)
+ {
+ if (initgroups(uname, gid) == -1)
+ syserr("include: initgroups(%s, %d) failed",
+ uname, gid);
+ }
+ else
+ {
+ GIDSET_T gidset[1];
+
+ gidset[0] = gid;
+ if (setgroups(1, gidset) == -1)
+ syserr("include: setgroups() failed");
+ }
+
+ if (gid != 0 && setgid(gid) < -1)
+ syserr("setgid(%d) failure", gid);
+ if (uid != 0)
+ {
+# if USESETEUID
+ if (seteuid(uid) < 0)
+ syserr("seteuid(%d) failure (real=%d, eff=%d)",
+ uid, getuid(), geteuid());
+# else
+ if (setreuid(0, uid) < 0)
+ syserr("setreuid(0, %d) failure (real=%d, eff=%d)",
+ uid, getuid(), geteuid());
+# endif
+ }
+ }
+#endif
+
+ if (tTd(27, 9))
+ printf("include: new uid = %d/%d\n",
+ (int) getuid(), (int) geteuid());
+
+ /*
+ ** If home directory is remote mounted but server is down,
+ ** this can hang or give errors; use a timeout to avoid this
+ */
+
+ if (setjmp(CtxIncludeTimeout) != 0)
+ {
+ ctladdr->q_flags |= QQUEUEUP;
+ errno = 0;
+
+ /* return pseudo-error code */
+ rval = E_SM_OPENTIMEOUT;
+ goto resetuid;
+ }
+ if (TimeOuts.to_fileopen > 0)
+ ev = setevent(TimeOuts.to_fileopen, includetimeout, 0);
+ else
+ ev = NULL;
+
+ /* check for writable parent directory */
+ p = strrchr(fname, '/');
+ if (p != NULL)
+ {
+ int ret;
+
+ *p = '\0';
+ ret = safedirpath(fname, uid, gid, uname, sfflags|SFF_SAFEDIRPATH);
+ if (ret == 0)
+ {
+ /* in safe directory: relax chown & link rules */
+ safedir = TRUE;
+ sfflags |= SFF_NOPATHCHECK;
+ }
+ else
+ {
+ if (bitset((forwarding ?
+ DBS_FORWARDFILEINUNSAFEDIRPATH :
+ DBS_INCLUDEFILEINUNSAFEDIRPATH),
+ DontBlameSendmail))
+ sfflags |= SFF_NOPATHCHECK;
+ else if (bitset((forwarding ?
+ DBS_FORWARDFILEINGROUPWRITABLEDIRPATH :
+ DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH),
+ DontBlameSendmail) &&
+ ret == E_SM_GWDIR)
+ {
+ DontBlameSendmail |= DBS_GROUPWRITABLEDIRPATHSAFE;
+ ret = safedirpath(fname, uid,
+ gid, uname,
+ sfflags|SFF_SAFEDIRPATH);
+ DontBlameSendmail &= ~DBS_GROUPWRITABLEDIRPATHSAFE;
+ if (ret == 0)
+ sfflags |= SFF_NOPATHCHECK;
+ else
+ sfflags |= SFF_SAFEDIRPATH;
+ }
+ else
+ sfflags |= SFF_SAFEDIRPATH;
+ if (ret > E_PSEUDOBASE &&
+ !bitset((forwarding ?
+ DBS_FORWARDFILEINUNSAFEDIRPATHSAFE :
+ DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE),
+ DontBlameSendmail))
+ {
+ if (LogLevel >= 12)
+ sm_syslog(LOG_INFO, e->e_id,
+ "%s: unsafe directory path, marked unsafe",
+ shortenstring(fname, MAXSHORTSTR));
+ ctladdr->q_flags |= QUNSAFEADDR;
+ }
+ }
+ *p = '/';
+ }
+
+ /* allow links only in unwritable directories */
+ if (!safedir &&
+ !bitset((forwarding ?
+ DBS_LINKEDFORWARDFILEINWRITABLEDIR :
+ DBS_LINKEDINCLUDEFILEINWRITABLEDIR),
+ DontBlameSendmail))
+ sfflags |= SFF_NOLINK;
+
+ rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD, &st);
+ if (rval != 0)
+ {
+ /* don't use this :include: file */
+ if (tTd(27, 4))
+ printf("include: not safe (uid=%d): %s\n",
+ (int) uid, errstring(rval));
+ }
+ else if ((fp = fopen(fname, "r")) == NULL)
+ {
+ rval = errno;
+ if (tTd(27, 4))
+ printf("include: open: %s\n", errstring(rval));
+ }
+ else if (filechanged(fname, fileno(fp), &st))
+ {
+ rval = E_SM_FILECHANGE;
+ if (tTd(27, 4))
+ printf("include: file changed after open\n");
+ }
+ if (ev != NULL)
+ clrevent(ev);
+
+resetuid:
+
+#if HASSETREUID || USESETEUID
+ if (saveduid == 0)
+ {
+ if (uid != 0)
+ {
+# if USESETEUID
+ if (seteuid(0) < 0)
+ syserr("seteuid(0) failure (real=%d, eff=%d)",
+ getuid(), geteuid());
+# else
+ if (setreuid(-1, 0) < 0)
+ syserr("setreuid(-1, 0) failure (real=%d, eff=%d)",
+ getuid(), geteuid());
+ if (setreuid(RealUid, 0) < 0)
+ syserr("setreuid(%d, 0) failure (real=%d, eff=%d)",
+ RealUid, getuid(), geteuid());
+# endif
+ }
+ setgid(savedgid);
+ }
+#endif
+
+ if (tTd(27, 9))
+ printf("include: reset uid = %d/%d\n",
+ (int) getuid(), (int) geteuid());
+
+ if (rval == E_SM_OPENTIMEOUT)
+ usrerr("451 open timeout on %s", fname);
+
+ if (fp == NULL)
+ return rval;
+
+ if (fstat(fileno(fp), &st) < 0)
+ {
+ rval = errno;
+ syserr("Cannot fstat %s!", fname);
+ return rval;
+ }
+
+ /* if path was writable, check to avoid file giveaway tricks */
+ safechown = chownsafe(fileno(fp), safedir);
+ if (tTd(27, 6))
+ printf("include: parent of %s is %s, chown is %ssafe\n",
+ fname,
+ safedir ? "safe" : "dangerous",
+ safechown ? "" : "un");
+
+ if (ca == NULL && safechown)
+ {
+ ctladdr->q_uid = st.st_uid;
+ ctladdr->q_gid = st.st_gid;
+ ctladdr->q_flags |= QGOODUID;
+ }
+ if (ca != NULL && ca->q_uid == st.st_uid)
+ {
+ /* optimization -- avoid getpwuid if we already have info */
+ ctladdr->q_flags |= ca->q_flags & QBOGUSSHELL;
+ ctladdr->q_ruser = ca->q_ruser;
+ }
+ else if (!forwarding)
+ {
+ register struct passwd *pw;
+
+ pw = sm_getpwuid(st.st_uid);
+ if (pw == NULL)
+ ctladdr->q_flags |= QBOGUSSHELL;
+ else
+ {
+ char *sh;
+
+ ctladdr->q_ruser = newstr(pw->pw_name);
+ if (safechown)
+ sh = pw->pw_shell;
+ else
+ sh = "/SENDMAIL/ANY/SHELL/";
+ if (!usershellok(pw->pw_name, sh))
+ {
+ if (LogLevel >= 12)
+ sm_syslog(LOG_INFO, e->e_id,
+ "%s: user %s has bad shell %s, marked %s",
+ shortenstring(fname, MAXSHORTSTR),
+ pw->pw_name, sh,
+ safechown ? "bogus" : "unsafe");
+ if (safechown)
+ ctladdr->q_flags |= QBOGUSSHELL;
+ else
+ ctladdr->q_flags |= QUNSAFEADDR;
+ }
+ }
+ }
+
+ if (bitset(EF_VRFYONLY, e->e_flags))
+ {
+ /* don't do any more now */
+ ctladdr->q_flags |= QVERIFIED;
+ e->e_nrcpts++;
+ xfclose(fp, "include", fname);
+ return rval;
+ }
+
+ /*
+ ** Check to see if some bad guy can write this file
+ **
+ ** Group write checking could be more clever, e.g.,
+ ** guessing as to which groups are actually safe ("sys"
+ ** may be; "user" probably is not).
+ */
+
+ mode = S_IWOTH;
+ if (!bitset((forwarding ?
+ DBS_GROUPWRITABLEFORWARDFILESAFE :
+ DBS_GROUPWRITABLEINCLUDEFILESAFE),
+ DontBlameSendmail))
+ mode |= S_IWGRP;
+
+ if (bitset(mode, st.st_mode))
+ {
+ if (tTd(27, 6))
+ printf("include: %s is %s writable, marked unsafe\n",
+ shortenstring(fname, MAXSHORTSTR),
+ bitset(S_IWOTH, st.st_mode) ? "world" : "group");
+ if (LogLevel >= 12)
+ sm_syslog(LOG_INFO, e->e_id,
+ "%s: %s writable %s file, marked unsafe",
+ shortenstring(fname, MAXSHORTSTR),
+ bitset(S_IWOTH, st.st_mode) ? "world" : "group",
+ forwarding ? "forward" : ":include:");
+ ctladdr->q_flags |= QUNSAFEADDR;
+ }
+
+ /* read the file -- each line is a comma-separated list. */
+ FileName = fname;
+ LineNumber = 0;
+ ctladdr->q_flags &= ~QSELFREF;
+ nincludes = 0;
+ while (fgets(buf, sizeof buf, fp) != NULL)
+ {
+ register char *p = strchr(buf, '\n');
+
+ LineNumber++;
+ if (p != NULL)
+ *p = '\0';
+ if (buf[0] == '#' || buf[0] == '\0')
+ continue;
+
+ /* <sp>#@# introduces a comment anywhere */
+ /* for Japanese character sets */
+ for (p = buf; (p = strchr(++p, '#')) != NULL; )
+ {
+ if (p[1] == '@' && p[2] == '#' &&
+ isascii(p[-1]) && isspace(p[-1]) &&
+ (p[3] == '\0' || (isascii(p[3]) && isspace(p[3]))))
+ {
+ p[-1] = '\0';
+ break;
+ }
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ e->e_to = NULL;
+ message("%s to %s",
+ forwarding ? "forwarding" : "sending", buf);
+ if (forwarding && LogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id,
+ "forward %.200s => %s",
+ oldto, shortenstring(buf, MAXSHORTSTR));
+
+ nincludes += sendtolist(buf, ctladdr, sendq, aliaslevel + 1, e);
+ }
+
+ if (ferror(fp) && tTd(27, 3))
+ printf("include: read error: %s\n", errstring(errno));
+ if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags))
+ {
+ if (tTd(27, 5))
+ {
+ printf("include: QDONTSEND ");
+ printaddr(ctladdr, FALSE);
+ }
+ ctladdr->q_flags |= QDONTSEND;
+ }
+
+ (void) xfclose(fp, "include", fname);
+ FileName = oldfilename;
+ LineNumber = oldlinenumber;
+ e->e_to = oldto;
+ return rval;
+}
+
+static void
+includetimeout()
+{
+ longjmp(CtxIncludeTimeout, 1);
+}
+ /*
+** SENDTOARGV -- send to an argument vector.
+**
+** Parameters:
+** argv -- argument vector to send to.
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** puts all addresses on the argument vector onto the
+** send queue.
+*/
+
+void
+sendtoargv(argv, e)
+ register char **argv;
+ register ENVELOPE *e;
+{
+ register char *p;
+
+ while ((p = *argv++) != NULL)
+ {
+ (void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e);
+ }
+}
+ /*
+** GETCTLADDR -- get controlling address from an address header.
+**
+** If none, get one corresponding to the effective userid.
+**
+** Parameters:
+** a -- the address to find the controller of.
+**
+** Returns:
+** the controlling address.
+**
+** Side Effects:
+** none.
+*/
+
+ADDRESS *
+getctladdr(a)
+ register ADDRESS *a;
+{
+ while (a != NULL && !bitset(QGOODUID, a->q_flags))
+ a = a->q_alias;
+ return (a);
+}
+ /*
+** SELF_REFERENCE -- check to see if an address references itself
+**
+** The check is done through a chain of aliases. If it is part of
+** a loop, break the loop at the "best" address, that is, the one
+** that exists as a real user.
+**
+** This is to handle the case of:
+** awc: Andrew.Chang
+** Andrew.Chang: awc@mail.server
+** which is a problem only on mail.server.
+**
+** Parameters:
+** a -- the address to check.
+** e -- the current envelope.
+**
+** Returns:
+** The address that should be retained.
+*/
+
+ADDRESS *
+self_reference(a, e)
+ ADDRESS *a;
+ ENVELOPE *e;
+{
+ ADDRESS *b; /* top entry in self ref loop */
+ ADDRESS *c; /* entry that point to a real mail box */
+
+ if (tTd(27, 1))
+ printf("self_reference(%s)\n", a->q_paddr);
+
+ for (b = a->q_alias; b != NULL; b = b->q_alias)
+ {
+ if (sameaddr(a, b))
+ break;
+ }
+
+ if (b == NULL)
+ {
+ if (tTd(27, 1))
+ printf("\t... no self ref\n");
+ return NULL;
+ }
+
+ /*
+ ** Pick the first address that resolved to a real mail box
+ ** i.e has a pw entry. The returned value will be marked
+ ** QSELFREF in recipient(), which in turn will disable alias()
+ ** from marking it QDONTSEND, which mean it will be used
+ ** as a deliverable address.
+ **
+ ** The 2 key thing to note here are:
+ ** 1) we are in a recursive call sequence:
+ ** alias->sentolist->recipient->alias
+ ** 2) normally, when we return back to alias(), the address
+ ** will be marked QDONTSEND, since alias() assumes the
+ ** expanded form will be used instead of the current address.
+ ** This behaviour is turned off if the address is marked
+ ** QSELFREF We set QSELFREF when we return to recipient().
+ */
+
+ c = a;
+ while (c != NULL)
+ {
+ if (bitnset(M_HASPWENT, c->q_mailer->m_flags))
+ {
+ if (tTd(27, 2))
+ printf("\t... getpwnam(%s)... ", c->q_user);
+ if (sm_getpwnam(c->q_user) != NULL)
+ {
+ if (tTd(27, 2))
+ printf("found\n");
+
+ /* ought to cache results here */
+ if (sameaddr(b, c))
+ return b;
+ else
+ return c;
+ }
+ if (tTd(27, 2))
+ printf("failed\n");
+ }
+ c = c->q_alias;
+ }
+
+ if (tTd(27, 1))
+ printf("\t... cannot break loop for \"%s\"\n", a->q_paddr);
+
+ return NULL;
+}
diff --git a/contrib/sendmail/src/safefile.c b/contrib/sendmail/src/safefile.c
new file mode 100644
index 0000000..16f3f39
--- /dev/null
+++ b/contrib/sendmail/src/safefile.c
@@ -0,0 +1,751 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)safefile.c 8.40 (Berkeley) 6/5/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+ /*
+** SAFEFILE -- return true if a file exists and is safe for a user.
+**
+** Parameters:
+** fn -- filename to check.
+** uid -- user id to compare against.
+** gid -- group id to compare against.
+** uname -- user name to compare against (used for group
+** sets).
+** flags -- modifiers:
+** SFF_MUSTOWN -- "uid" must own this file.
+** SFF_NOSLINK -- file cannot be a symbolic link.
+** mode -- mode bits that must match.
+** st -- if set, points to a stat structure that will
+** get the stat info for the file.
+**
+** Returns:
+** 0 if fn exists, is owned by uid, and matches mode.
+** An errno otherwise. The actual errno is cleared.
+**
+** Side Effects:
+** none.
+*/
+
+#include <grp.h>
+
+int
+safefile(fn, uid, gid, uname, flags, mode, st)
+ char *fn;
+ UID_T uid;
+ GID_T gid;
+ char *uname;
+ int flags;
+ int mode;
+ struct stat *st;
+{
+ register char *p;
+ register struct group *gr = NULL;
+ int file_errno = 0;
+ bool checkpath;
+ struct stat stbuf;
+ struct stat fstbuf;
+ char fbuf[MAXPATHLEN + 1];
+
+ if (tTd(44, 4))
+ printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
+ fn, (int) uid, (int) gid, flags, mode);
+ errno = 0;
+ if (st == NULL)
+ st = &fstbuf;
+ if (strlen(fn) > sizeof fbuf - 1)
+ {
+ if (tTd(44, 4))
+ printf("\tpathname too long\n");
+ return ENAMETOOLONG;
+ }
+ strcpy(fbuf, fn);
+ fn = fbuf;
+
+ /* ignore SFF_SAFEDIRPATH if we are debugging */
+ if (RealUid != 0 && RunAsUid == RealUid)
+ flags &= ~SFF_SAFEDIRPATH;
+
+ /* first check to see if the file exists at all */
+#ifdef HASLSTAT
+ if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
+ : stat(fn, st)) < 0)
+#else
+ if (stat(fn, st) < 0)
+#endif
+ {
+ file_errno = errno;
+ }
+ else if (bitset(SFF_SETUIDOK, flags) &&
+ !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
+ S_ISREG(st->st_mode))
+ {
+ /*
+ ** If final file is setuid, run as the owner of that
+ ** file. Gotta be careful not to reveal anything too
+ ** soon here!
+ */
+
+#ifdef SUID_ROOT_FILES_OK
+ if (bitset(S_ISUID, st->st_mode))
+#else
+ if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
+ st->st_uid != TrustedFileUid)
+#endif
+ {
+ uid = st->st_uid;
+ uname = NULL;
+ }
+#ifdef SUID_ROOT_FILES_OK
+ if (bitset(S_ISGID, st->st_mode))
+#else
+ if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
+#endif
+ gid = st->st_gid;
+ }
+
+ checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
+ (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
+ if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
+ {
+ int ret;
+
+ /* check the directory */
+ p = strrchr(fn, '/');
+ if (p == NULL)
+ {
+ ret = safedirpath(".", uid, gid, uname, flags|SFF_SAFEDIRPATH);
+ }
+ else
+ {
+ *p = '\0';
+ ret = safedirpath(fn, uid, gid, uname, flags|SFF_SAFEDIRPATH);
+ *p = '/';
+ }
+ if (ret == 0)
+ {
+ /* directory is safe */
+ checkpath = FALSE;
+ }
+ else
+ {
+#ifdef HASLSTAT
+ /* Need lstat() information if called stat() before */
+ if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
+ {
+ ret = errno;
+ if (tTd(44, 4))
+ printf("\t%s\n", errstring(ret));
+ return ret;
+ }
+#endif
+ /* directory is writable: disallow links */
+ flags |= SFF_NOLINK;
+ }
+ }
+
+ if (checkpath)
+ {
+ int ret;
+
+ p = strrchr(fn, '/');
+ if (p == NULL)
+ {
+ ret = safedirpath(".", uid, gid, uname, flags);
+ }
+ else
+ {
+ *p = '\0';
+ ret = safedirpath(fn, uid, gid, uname, flags);
+ *p = '/';
+ }
+ if (ret != 0)
+ return ret;
+ }
+
+ /*
+ ** If the target file doesn't exist, check the directory to
+ ** ensure that it is writable by this user.
+ */
+
+ if (file_errno != 0)
+ {
+ int ret = file_errno;
+ char *dir = fn;
+
+ if (tTd(44, 4))
+ printf("\t%s\n", errstring(ret));
+
+ errno = 0;
+ if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
+ return ret;
+
+ /* check to see if legal to create the file */
+ p = strrchr(dir, '/');
+ if (p == NULL)
+ dir = ".";
+ else if (p == dir)
+ dir = "/";
+ else
+ *p = '\0';
+ if (stat(dir, &stbuf) >= 0)
+ {
+ int md = S_IWRITE|S_IEXEC;
+
+ if (stbuf.st_uid == uid)
+ ;
+ else if (uid == 0 && TrustedFileUid != 0 && stbuf.st_uid == TrustedFileUid)
+ ;
+ else
+ {
+ md >>= 3;
+ if (stbuf.st_gid == gid)
+ ;
+#ifndef NO_GROUP_SET
+ else if (uname != NULL && !DontInitGroups &&
+ ((gr != NULL &&
+ gr->gr_gid == stbuf.st_gid) ||
+ (gr = getgrgid(stbuf.st_gid)) != NULL))
+ {
+ register char **gp;
+
+ for (gp = gr->gr_mem; *gp != NULL; gp++)
+ if (strcmp(*gp, uname) == 0)
+ break;
+ if (*gp == NULL)
+ md >>= 3;
+ }
+#endif
+ else
+ md >>= 3;
+ }
+ if ((stbuf.st_mode & md) != md)
+ errno = EACCES;
+ }
+ ret = errno;
+ if (tTd(44, 4))
+ printf("\t[final dir %s uid %d mode %lo] %s\n",
+ dir, (int) stbuf.st_uid, (u_long) stbuf.st_mode,
+ errstring(ret));
+ if (p != NULL)
+ *p = '/';
+ st->st_mode = ST_MODE_NOFILE;
+ return ret;
+ }
+
+#ifdef S_ISLNK
+ if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
+ {
+ if (tTd(44, 4))
+ printf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
+ (u_long) st->st_mode);
+ return E_SM_NOSLINK;
+ }
+#endif
+ if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
+ {
+ if (tTd(44, 4))
+ printf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
+ (u_long) st->st_mode);
+ return E_SM_REGONLY;
+ }
+ if (bitset(SFF_NOGWFILES, flags) &&
+ bitset(S_IWGRP, st->st_mode))
+ {
+ if (tTd(44, 4))
+ printf("\t[write bits %lo]\tE_SM_GWFILE\n",
+ (u_long) st->st_mode);
+ return E_SM_GWFILE;
+ }
+ if (bitset(SFF_NOWWFILES, flags) &&
+ bitset(S_IWOTH, st->st_mode))
+ {
+ if (tTd(44, 4))
+ printf("\t[write bits %lo]\tE_SM_WWFILE\n",
+ (u_long) st->st_mode);
+ return E_SM_WWFILE;
+ }
+ if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
+ bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
+ {
+ if (tTd(44, 4))
+ printf("\t[exec bits %lo]\tE_SM_ISEXEC]\n",
+ (u_long) st->st_mode);
+ return E_SM_ISEXEC;
+ }
+ if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
+ {
+ if (tTd(44, 4))
+ printf("\t[link count %d]\tE_SM_NOHLINK\n",
+ (int) st->st_nlink);
+ return E_SM_NOHLINK;
+ }
+
+ if (uid == 0 && bitset(SFF_OPENASROOT, flags))
+ ;
+ else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
+ mode >>= 6;
+ else if (st->st_uid == uid)
+ ;
+ else if (uid == 0 && TrustedFileUid != 0 && st->st_uid == TrustedFileUid)
+ ;
+ else
+ {
+ mode >>= 3;
+ if (st->st_gid == gid)
+ ;
+#ifndef NO_GROUP_SET
+ else if (uname != NULL && !DontInitGroups &&
+ ((gr != NULL && gr->gr_gid == st->st_gid) ||
+ (gr = getgrgid(st->st_gid)) != NULL))
+ {
+ register char **gp;
+
+ for (gp = gr->gr_mem; *gp != NULL; gp++)
+ if (strcmp(*gp, uname) == 0)
+ break;
+ if (*gp == NULL)
+ mode >>= 3;
+ }
+#endif
+ else
+ mode >>= 3;
+ }
+ if (tTd(44, 4))
+ printf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
+ (int) st->st_uid, (int) st->st_nlink,
+ (u_long) st->st_mode, (u_long) mode);
+ if ((st->st_uid == uid || st->st_uid == 0 ||
+ st->st_uid == TrustedFileUid ||
+ !bitset(SFF_MUSTOWN, flags)) &&
+ (st->st_mode & mode) == mode)
+ {
+ if (tTd(44, 4))
+ printf("\tOK\n");
+ return 0;
+ }
+ if (tTd(44, 4))
+ printf("\tEACCES\n");
+ return EACCES;
+}
+ /*
+** SAFEDIRPATH -- check to make sure a path to a directory is safe
+**
+** Safe means not writable and owned by the right folks.
+**
+** Parameters:
+** fn -- filename to check.
+** uid -- user id to compare against.
+** gid -- group id to compare against.
+** uname -- user name to compare against (used for group
+** sets).
+** flags -- modifiers:
+** SFF_ROOTOK -- ok to use root permissions to open.
+** SFF_SAFEDIRPATH -- writable directories are considered
+** to be fatal errors.
+**
+** Returns:
+** 0 -- if the directory path is "safe".
+** else -- an error number associated with the path.
+*/
+
+int
+safedirpath(fn, uid, gid, uname, flags)
+ char *fn;
+ UID_T uid;
+ GID_T gid;
+ char *uname;
+ int flags;
+{
+ char *p;
+ register struct group *gr = NULL;
+ int ret = 0;
+ int mode = S_IWOTH;
+ struct stat stbuf;
+
+ /* special case root directory */
+ if (*fn == '\0')
+ fn = "/";
+
+ if (tTd(44, 4))
+ printf("safedirpath(%s, uid=%ld, gid=%ld, flags=%x):\n",
+ fn, (long) uid, (long) gid, flags);
+
+ if (!bitset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
+ mode |= S_IWGRP;
+
+ p = fn;
+ do
+ {
+ if (*p == '\0')
+ *p = '/';
+ p = strchr(++p, '/');
+ if (p != NULL)
+ *p = '\0';
+ if (stat(fn, &stbuf) < 0)
+ {
+ ret = errno;
+ break;
+ }
+ if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
+ bitset(mode, stbuf.st_mode))
+ {
+ if (tTd(44, 4))
+ printf("\t[dir %s] mode %lo\n",
+ fn, (u_long) stbuf.st_mode);
+ if (bitset(SFF_SAFEDIRPATH, flags))
+ {
+ if (bitset(S_IWOTH, stbuf.st_mode))
+ ret = E_SM_WWDIR;
+ else
+ ret = E_SM_GWDIR;
+ break;
+ }
+ if (Verbose > 1)
+ message("051 WARNING: %s writable directory %s",
+ bitset(S_IWOTH, stbuf.st_mode)
+ ? "World"
+ : "Group",
+ fn);
+ }
+ if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
+ {
+ if (bitset(S_IXOTH, stbuf.st_mode))
+ continue;
+ ret = EACCES;
+ break;
+ }
+
+ /*
+ ** Let OS determine access to file if we are not
+ ** running as a privileged user. This allows ACLs
+ ** to work.
+ */
+ if (geteuid() != 0)
+ continue;
+
+ if (stbuf.st_uid == uid &&
+ bitset(S_IXUSR, stbuf.st_mode))
+ continue;
+ if (stbuf.st_gid == gid &&
+ bitset(S_IXGRP, stbuf.st_mode))
+ continue;
+#ifndef NO_GROUP_SET
+ if (uname != NULL && !DontInitGroups &&
+ ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
+ (gr = getgrgid(stbuf.st_gid)) != NULL))
+ {
+ register char **gp;
+
+ for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
+ if (strcmp(*gp, uname) == 0)
+ break;
+ if (gp != NULL && *gp != NULL &&
+ bitset(S_IXGRP, stbuf.st_mode))
+ continue;
+ }
+#endif
+ if (!bitset(S_IXOTH, stbuf.st_mode))
+ {
+ ret = EACCES;
+ break;
+ }
+ } while (p != NULL);
+ if (ret != 0 && tTd(44, 4))
+ printf("\t[dir %s] %s\n", fn, errstring(ret));
+ if (p != NULL)
+ *p = '/';
+ return ret;
+}
+ /*
+** SAFEOPEN -- do a file open with extra checking
+**
+** Parameters:
+** fn -- the file name to open.
+** omode -- the open-style mode flags.
+** cmode -- the create-style mode flags.
+** sff -- safefile flags.
+**
+** Returns:
+** Same as open.
+*/
+
+#ifndef O_ACCMODE
+# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
+#endif
+
+int
+safeopen(fn, omode, cmode, sff)
+ char *fn;
+ int omode;
+ int cmode;
+ int sff;
+{
+ int rval;
+ int fd;
+ int smode;
+ struct stat stb;
+
+ if (bitset(O_CREAT, omode))
+ sff |= SFF_CREAT;
+ omode &= ~O_CREAT;
+ smode = 0;
+ switch (omode & O_ACCMODE)
+ {
+ case O_RDONLY:
+ smode = S_IREAD;
+ break;
+
+ case O_WRONLY:
+ smode = S_IWRITE;
+ break;
+
+ case O_RDWR:
+ smode = S_IREAD|S_IWRITE;
+ break;
+
+ default:
+ smode = 0;
+ break;
+ }
+ if (bitset(SFF_OPENASROOT, sff))
+ rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
+ sff, smode, &stb);
+ else
+ rval = safefile(fn, RealUid, RealGid, RealUserName,
+ sff, smode, &stb);
+ if (rval != 0)
+ {
+ errno = rval;
+ return -1;
+ }
+ if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
+ omode |= O_EXCL|O_CREAT;
+
+ fd = dfopen(fn, omode, cmode, sff);
+ if (fd < 0)
+ return fd;
+ if (filechanged(fn, fd, &stb))
+ {
+ syserr("554 cannot open: file %s changed after open", fn);
+ close(fd);
+ errno = E_SM_FILECHANGE;
+ return -1;
+ }
+ return fd;
+}
+ /*
+** SAFEFOPEN -- do a file open with extra checking
+**
+** Parameters:
+** fn -- the file name to open.
+** omode -- the open-style mode flags.
+** cmode -- the create-style mode flags.
+** sff -- safefile flags.
+**
+** Returns:
+** Same as fopen.
+*/
+
+FILE *
+safefopen(fn, omode, cmode, sff)
+ char *fn;
+ int omode;
+ int cmode;
+ int sff;
+{
+ int fd;
+ FILE *fp;
+ char *fmode;
+
+ switch (omode & O_ACCMODE)
+ {
+ case O_RDONLY:
+ fmode = "r";
+ break;
+
+ case O_WRONLY:
+ if (bitset(O_APPEND, omode))
+ fmode = "a";
+ else
+ fmode = "w";
+ break;
+
+ case O_RDWR:
+ if (bitset(O_TRUNC, omode))
+ fmode = "w+";
+ else if (bitset(O_APPEND, omode))
+ fmode = "a+";
+ else
+ fmode = "r+";
+ break;
+
+ default:
+ syserr("safefopen: unknown omode %o", omode);
+ fmode = "x";
+ }
+ fd = safeopen(fn, omode, cmode, sff);
+ if (fd < 0)
+ {
+ if (tTd(44, 10))
+ printf("safefopen: safeopen failed: %s\n",
+ errstring(errno));
+ return NULL;
+ }
+ fp = fdopen(fd, fmode);
+ if (fp != NULL)
+ return fp;
+
+ if (tTd(44, 10))
+ {
+ printf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%x, err=%s\n",
+ fn, fmode, omode, sff, errstring(errno));
+#ifndef NOT_SENDMAIL
+ dumpfd(fd, TRUE, FALSE);
+#endif
+ }
+ (void) close(fd);
+ return NULL;
+}
+ /*
+** FILECHANGED -- check to see if file changed after being opened
+**
+** Parameters:
+** fn -- pathname of file to check.
+** fd -- file descriptor to check.
+** stb -- stat structure from before open.
+**
+** Returns:
+** TRUE -- if a problem was detected.
+** FALSE -- if this file is still the same.
+*/
+
+bool
+filechanged(fn, fd, stb)
+ char *fn;
+ int fd;
+ struct stat *stb;
+{
+ struct stat sta;
+
+ if (stb->st_mode == ST_MODE_NOFILE)
+ {
+#if HASLSTAT && BOGUS_O_EXCL
+ /* only necessary if exclusive open follows symbolic links */
+ if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
+ return TRUE;
+#else
+ return FALSE;
+#endif
+ }
+ if (fstat(fd, &sta) < 0)
+ return TRUE;
+
+ if (sta.st_nlink != stb->st_nlink ||
+ sta.st_dev != stb->st_dev ||
+ sta.st_ino != stb->st_ino ||
+#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
+ sta.st_gen != stb->st_gen ||
+#endif
+ sta.st_uid != stb->st_uid ||
+ sta.st_gid != stb->st_gid)
+ {
+ if (tTd(44, 8))
+ {
+ printf("File changed after opening:\n");
+ printf(" nlink = %ld/%ld\n",
+ (long) stb->st_nlink, (long) sta.st_nlink);
+ printf(" dev = %ld/%ld\n",
+ (long) stb->st_dev, (long) sta.st_dev);
+ if (sizeof sta.st_ino > sizeof (long))
+ {
+ printf(" ino = %s/",
+ quad_to_string(stb->st_ino));
+ printf("%s\n",
+ quad_to_string(sta.st_ino));
+ }
+ else
+ printf(" ino = %lu/%lu\n",
+ (unsigned long) stb->st_ino,
+ (unsigned long) sta.st_ino);
+#if HAS_ST_GEN
+ printf(" gen = %ld/%ld\n",
+ (long) stb->st_gen, (long) sta.st_gen);
+#endif
+ printf(" uid = %ld/%ld\n",
+ (long) stb->st_uid, (long) sta.st_uid);
+ printf(" gid = %ld/%ld\n",
+ (long) stb->st_gid, (long) sta.st_gid);
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+ /*
+** DFOPEN -- determined file open
+**
+** This routine has the semantics of open, except that it will
+** keep trying a few times to make this happen. The idea is that
+** on very loaded systems, we may run out of resources (inodes,
+** whatever), so this tries to get around it.
+*/
+
+int
+dfopen(filename, omode, cmode, sff)
+ char *filename;
+ int omode;
+ int cmode;
+ int sff;
+{
+ register int tries;
+ int fd;
+ struct stat st;
+
+ for (tries = 0; tries < 10; tries++)
+ {
+ sleep((unsigned) (10 * tries));
+ errno = 0;
+ fd = open(filename, omode, cmode);
+ if (fd >= 0)
+ break;
+ switch (errno)
+ {
+ case ENFILE: /* system file table full */
+ case EINTR: /* interrupted syscall */
+#ifdef ETXTBSY
+ case ETXTBSY: /* Apollo: net file locked */
+#endif
+ continue;
+ }
+ break;
+ }
+ if (!bitset(SFF_NOLOCK, sff) &&
+ fd >= 0 &&
+ fstat(fd, &st) >= 0 &&
+ S_ISREG(st.st_mode))
+ {
+ int locktype;
+
+ /* lock the file to avoid accidental conflicts */
+ if ((omode & O_ACCMODE) != O_RDONLY)
+ locktype = LOCK_EX;
+ else
+ locktype = LOCK_SH;
+ (void) lockfile(fd, filename, NULL, locktype);
+ errno = 0;
+ }
+ return fd;
+}
diff --git a/contrib/sendmail/src/savemail.c b/contrib/sendmail/src/savemail.c
new file mode 100644
index 0000000..4fbfd67
--- /dev/null
+++ b/contrib/sendmail/src/savemail.c
@@ -0,0 +1,1487 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)savemail.c 8.138 (Berkeley) 6/17/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+** SAVEMAIL -- Save mail on error
+**
+** If mailing back errors, mail it back to the originator
+** together with an error message; otherwise, just put it in
+** dead.letter in the user's home directory (if he exists on
+** this machine).
+**
+** Parameters:
+** e -- the envelope containing the message in error.
+** sendbody -- if TRUE, also send back the body of the
+** message; otherwise just send the header.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Saves the letter, by writing or mailing it back to the
+** sender, or by putting it in dead.letter in her home
+** directory.
+*/
+
+/* defines for state machine */
+# define ESM_REPORT 0 /* report to sender's terminal */
+# define ESM_MAIL 1 /* mail back to sender */
+# define ESM_QUIET 2 /* messages have already been returned */
+# define ESM_DEADLETTER 3 /* save in ~/dead.letter */
+# define ESM_POSTMASTER 4 /* return to postmaster */
+# define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */
+# define ESM_PANIC 6 /* leave the locked queue/transcript files */
+# define ESM_DONE 7 /* the message is successfully delivered */
+
+
+void
+savemail(e, sendbody)
+ register ENVELOPE *e;
+ bool sendbody;
+{
+ register struct passwd *pw;
+ register FILE *fp;
+ int state;
+ auto ADDRESS *q = NULL;
+ register char *p;
+ MCI mcibuf;
+ int flags;
+ char buf[MAXLINE+1];
+ extern char *ttypath __P((void));
+ extern bool writable __P((char *, ADDRESS *, int));
+
+ if (tTd(6, 1))
+ {
+ printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=",
+ e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
+ ExitStat);
+ printaddr(&e->e_from, FALSE);
+ }
+
+ if (e->e_id == NULL)
+ {
+ /* can't return a message with no id */
+ return;
+ }
+
+ /*
+ ** In the unhappy event we don't know who to return the mail
+ ** to, make someone up.
+ */
+
+ if (e->e_from.q_paddr == NULL)
+ {
+ e->e_sender = "Postmaster";
+ if (parseaddr(e->e_sender, &e->e_from,
+ RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL)
+ {
+ syserr("553 Cannot parse Postmaster!");
+ ExitStat = EX_SOFTWARE;
+ finis();
+ }
+ }
+ e->e_to = NULL;
+
+ /*
+ ** Basic state machine.
+ **
+ ** This machine runs through the following states:
+ **
+ ** ESM_QUIET Errors have already been printed iff the
+ ** sender is local.
+ ** ESM_REPORT Report directly to the sender's terminal.
+ ** ESM_MAIL Mail response to the sender.
+ ** ESM_DEADLETTER Save response in ~/dead.letter.
+ ** ESM_POSTMASTER Mail response to the postmaster.
+ ** ESM_PANIC Save response anywhere possible.
+ */
+
+ /* determine starting state */
+ switch (e->e_errormode)
+ {
+ case EM_WRITE:
+ state = ESM_REPORT;
+ break;
+
+ case EM_BERKNET:
+ case EM_MAIL:
+ state = ESM_MAIL;
+ break;
+
+ case EM_PRINT:
+ case '\0':
+ state = ESM_QUIET;
+ break;
+
+ case EM_QUIET:
+ /* no need to return anything at all */
+ return;
+
+ default:
+ syserr("554 savemail: bogus errormode x%x\n", e->e_errormode);
+ state = ESM_MAIL;
+ break;
+ }
+
+ /* if this is already an error response, send to postmaster */
+ if (bitset(EF_RESPONSE, e->e_flags))
+ {
+ if (e->e_parent != NULL &&
+ bitset(EF_RESPONSE, e->e_parent->e_flags))
+ {
+ /* got an error sending a response -- can it */
+ return;
+ }
+ state = ESM_POSTMASTER;
+ }
+
+ while (state != ESM_DONE)
+ {
+ if (tTd(6, 5))
+ printf(" state %d\n", state);
+
+ switch (state)
+ {
+ case ESM_QUIET:
+ if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
+ state = ESM_DEADLETTER;
+ else
+ state = ESM_MAIL;
+ break;
+
+ case ESM_REPORT:
+
+ /*
+ ** If the user is still logged in on the same terminal,
+ ** then write the error messages back to hir (sic).
+ */
+
+ p = ttypath();
+ if (p == NULL || freopen(p, "w", stdout) == NULL)
+ {
+ state = ESM_MAIL;
+ break;
+ }
+
+ expand("\201n", buf, sizeof buf, e);
+ printf("\r\nMessage from %s...\r\n", buf);
+ printf("Errors occurred while sending mail.\r\n");
+ if (e->e_xfp != NULL)
+ {
+ (void) fflush(e->e_xfp);
+ fp = fopen(queuename(e, 'x'), "r");
+ }
+ else
+ fp = NULL;
+ if (fp == NULL)
+ {
+ syserr("Cannot open %s", queuename(e, 'x'));
+ printf("Transcript of session is unavailable.\r\n");
+ }
+ else
+ {
+ printf("Transcript follows:\r\n");
+ while (fgets(buf, sizeof buf, fp) != NULL &&
+ !ferror(stdout))
+ fputs(buf, stdout);
+ (void) xfclose(fp, "savemail transcript", e->e_id);
+ }
+ printf("Original message will be saved in dead.letter.\r\n");
+ state = ESM_DEADLETTER;
+ break;
+
+ case ESM_MAIL:
+ /*
+ ** If mailing back, do it.
+ ** Throw away all further output. Don't alias,
+ ** since this could cause loops, e.g., if joe
+ ** mails to joe@x, and for some reason the network
+ ** for @x is down, then the response gets sent to
+ ** joe@x, which gives a response, etc. Also force
+ ** the mail to be delivered even if a version of
+ ** it has already been sent to the sender.
+ **
+ ** If this is a configuration or local software
+ ** error, send to the local postmaster as well,
+ ** since the originator can't do anything
+ ** about it anyway. Note that this is a full
+ ** copy of the message (intentionally) so that
+ ** the Postmaster can forward things along.
+ */
+
+ if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
+ {
+ (void) sendtolist("postmaster",
+ NULLADDR, &e->e_errorqueue, 0, e);
+ }
+ if (!emptyaddr(&e->e_from))
+ {
+ char from[TOBUFSIZE];
+ extern bool pruneroute __P((char *));
+
+ if (strlen(e->e_from.q_paddr) + 1 > sizeof from)
+ {
+ state = ESM_POSTMASTER;
+ break;
+ }
+ strcpy(from, e->e_from.q_paddr);
+
+ if (!DontPruneRoutes && pruneroute(from))
+ {
+ ADDRESS *a;
+
+ for (a = e->e_errorqueue; a != NULL;
+ a = a->q_next)
+ {
+ if (sameaddr(a, &e->e_from))
+ a->q_flags |= QDONTSEND;
+ }
+ }
+ (void) sendtolist(from, NULLADDR,
+ &e->e_errorqueue, 0, e);
+ }
+
+ /*
+ ** Deliver a non-delivery report to the
+ ** Postmaster-designate (not necessarily
+ ** Postmaster). This does not include the
+ ** body of the message, for privacy reasons.
+ ** You really shouldn't need this.
+ */
+
+ e->e_flags |= EF_PM_NOTIFY;
+
+ /* check to see if there are any good addresses */
+ for (q = e->e_errorqueue; q != NULL; q = q->q_next)
+ if (!bitset(QBADADDR|QDONTSEND, q->q_flags))
+ break;
+ if (q == NULL)
+ {
+ /* this is an error-error */
+ state = ESM_POSTMASTER;
+ break;
+ }
+ if (returntosender(e->e_message, e->e_errorqueue,
+ sendbody ? RTSF_SEND_BODY
+ : RTSF_NO_BODY,
+ e) == 0)
+ {
+ state = ESM_DONE;
+ break;
+ }
+
+ /* didn't work -- return to postmaster */
+ state = ESM_POSTMASTER;
+ break;
+
+ case ESM_POSTMASTER:
+ /*
+ ** Similar to previous case, but to system postmaster.
+ */
+
+ q = NULL;
+ if (sendtolist(DoubleBounceAddr,
+ NULLADDR, &q, 0, e) <= 0)
+ {
+ syserr("553 cannot parse %s!", DoubleBounceAddr);
+ ExitStat = EX_SOFTWARE;
+ state = ESM_USRTMP;
+ break;
+ }
+ flags = RTSF_PM_BOUNCE;
+ if (sendbody)
+ flags |= RTSF_SEND_BODY;
+ if (returntosender(e->e_message, q, flags, e) == 0)
+ {
+ state = ESM_DONE;
+ break;
+ }
+
+ /* didn't work -- last resort */
+ state = ESM_USRTMP;
+ break;
+
+ case ESM_DEADLETTER:
+ /*
+ ** Save the message in dead.letter.
+ ** If we weren't mailing back, and the user is
+ ** local, we should save the message in
+ ** ~/dead.letter so that the poor person doesn't
+ ** have to type it over again -- and we all know
+ ** what poor typists UNIX users are.
+ */
+
+ p = NULL;
+ if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
+ {
+ if (e->e_from.q_home != NULL)
+ p = e->e_from.q_home;
+ else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL)
+ p = pw->pw_dir;
+ }
+ if (p == NULL || e->e_dfp == NULL)
+ {
+ /* no local directory or no data file */
+ state = ESM_MAIL;
+ break;
+ }
+
+ /* we have a home directory; write dead.letter */
+ define('z', p, e);
+ expand("\201z/dead.letter", buf, sizeof buf, e);
+ flags = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
+ if (RealUid == 0)
+ flags |= SFF_ROOTOK;
+ e->e_to = buf;
+ if (mailfile(buf, FileMailer, NULL, flags, e) == EX_OK)
+ {
+ int oldverb = Verbose;
+
+ Verbose = 1;
+ message("Saved message in %s", buf);
+ Verbose = oldverb;
+ state = ESM_DONE;
+ break;
+ }
+ state = ESM_MAIL;
+ break;
+
+ case ESM_USRTMP:
+ /*
+ ** Log the mail in /usr/tmp/dead.letter.
+ */
+
+ if (e->e_class < 0)
+ {
+ state = ESM_DONE;
+ break;
+ }
+
+ if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
+ DeadLetterDrop == NULL || DeadLetterDrop[0] == '\0')
+ {
+ state = ESM_PANIC;
+ break;
+ }
+
+ flags = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
+ if (!writable(DeadLetterDrop, NULL, flags) ||
+ (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
+ FileMode, flags)) == NULL)
+ {
+ state = ESM_PANIC;
+ break;
+ }
+
+ bzero(&mcibuf, sizeof mcibuf);
+ mcibuf.mci_out = fp;
+ mcibuf.mci_mailer = FileMailer;
+ if (bitnset(M_7BITS, FileMailer->m_flags))
+ mcibuf.mci_flags |= MCIF_7BIT;
+ mcibuf.mci_contentlen = 0;
+
+ putfromline(&mcibuf, e);
+ (*e->e_puthdr)(&mcibuf, e->e_header, e);
+ (*e->e_putbody)(&mcibuf, e, NULL);
+ putline("\n", &mcibuf);
+ (void) fflush(fp);
+ if (ferror(fp))
+ state = ESM_PANIC;
+ else
+ {
+ int oldverb = Verbose;
+
+ Verbose = 1;
+ message("Saved message in %s", DeadLetterDrop);
+ Verbose = oldverb;
+ if (LogLevel > 3)
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "Saved message in %s",
+ DeadLetterDrop);
+ state = ESM_DONE;
+ }
+ (void) xfclose(fp, "savemail", DeadLetterDrop);
+ break;
+
+ default:
+ syserr("554 savemail: unknown state %d", state);
+
+ /* fall through ... */
+
+ case ESM_PANIC:
+ /* leave the locked queue & transcript files around */
+ loseqfile(e, "savemail panic");
+ syserr("!554 savemail: cannot save rejected email anywhere");
+ }
+ }
+}
+ /*
+** RETURNTOSENDER -- return a message to the sender with an error.
+**
+** Parameters:
+** msg -- the explanatory message.
+** returnq -- the queue of people to send the message to.
+** flags -- flags tweaking the operation:
+** RTSF_SENDBODY -- include body of message (otherwise
+** just send the header).
+** RTSF_PMBOUNCE -- this is a postmaster bounce.
+** e -- the current envelope.
+**
+** Returns:
+** zero -- if everything went ok.
+** else -- some error.
+**
+** Side Effects:
+** Returns the current message to the sender via
+** mail.
+*/
+
+#define MAXRETURNS 6 /* max depth of returning messages */
+#define ERRORFUDGE 100 /* nominal size of error message text */
+
+int
+returntosender(msg, returnq, flags, e)
+ char *msg;
+ ADDRESS *returnq;
+ int flags;
+ register ENVELOPE *e;
+{
+ register ENVELOPE *ee;
+ ENVELOPE *oldcur = CurEnv;
+ ENVELOPE errenvelope;
+ static int returndepth = 0;
+ register ADDRESS *q;
+ char *p;
+ char buf[MAXNAME + 1];
+ extern void errbody __P((MCI *, ENVELOPE *, char *));
+
+ if (returnq == NULL)
+ return (-1);
+
+ if (msg == NULL)
+ msg = "Unable to deliver mail";
+
+ if (tTd(6, 1))
+ {
+ printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=",
+ msg, returndepth, (u_long) e);
+ printaddr(returnq, TRUE);
+ if (tTd(6, 20))
+ {
+ printf("Sendq=");
+ printaddr(e->e_sendqueue, TRUE);
+ }
+ }
+
+ if (++returndepth >= MAXRETURNS)
+ {
+ if (returndepth != MAXRETURNS)
+ syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr);
+ /* don't "unrecurse" and fake a clean exit */
+ /* returndepth--; */
+ return (0);
+ }
+
+ define('g', e->e_from.q_paddr, e);
+ define('u', NULL, e);
+
+ /* initialize error envelope */
+ ee = newenvelope(&errenvelope, e);
+ define('a', "\201b", ee);
+ define('r', "internal", ee);
+ define('s', "localhost", ee);
+ define('_', "localhost", ee);
+ ee->e_puthdr = putheader;
+ ee->e_putbody = errbody;
+ ee->e_flags |= EF_RESPONSE|EF_METOO;
+ if (!bitset(EF_OLDSTYLE, e->e_flags))
+ ee->e_flags &= ~EF_OLDSTYLE;
+ ee->e_sendqueue = returnq;
+ ee->e_msgsize = ERRORFUDGE;
+ if (bitset(RTSF_SEND_BODY, flags))
+ ee->e_msgsize += e->e_msgsize;
+ else
+ ee->e_flags |= EF_NO_BODY_RETN;
+ initsys(ee);
+ for (q = returnq; q != NULL; q = q->q_next)
+ {
+ if (bitset(QBADADDR, q->q_flags))
+ continue;
+
+ q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
+ q->q_flags |= QPINGONFAILURE;
+
+ if (!bitset(QDONTSEND, q->q_flags))
+ ee->e_nrcpts++;
+
+ if (q->q_alias == NULL)
+ addheader("To", q->q_paddr, &ee->e_header);
+ }
+
+ if (LogLevel > 5)
+ {
+ if (bitset(EF_RESPONSE|EF_WARNING, e->e_flags))
+ p = "return to sender";
+ else if (bitset(RTSF_PM_BOUNCE, flags))
+ p = "postmaster notify";
+ else
+ p = "DSN";
+ sm_syslog(LOG_INFO, e->e_id,
+ "%s: %s: %s",
+ ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
+ }
+
+ if (SendMIMEErrors)
+ {
+ addheader("MIME-Version", "1.0", &ee->e_header);
+
+ (void) snprintf(buf, sizeof buf, "%s.%ld/%.100s",
+ ee->e_id, curtime(), MyHostName);
+ ee->e_msgboundary = newstr(buf);
+ (void) snprintf(buf, sizeof buf,
+#if DSN
+ "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
+#else
+ "multipart/mixed; boundary=\"%s\"",
+#endif
+ ee->e_msgboundary);
+ addheader("Content-Type", buf, &ee->e_header);
+
+ p = hvalue("Content-Transfer-Encoding", e->e_header);
+ if (p != NULL && strcasecmp(p, "binary") != 0)
+ p = NULL;
+ if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
+ p = "8bit";
+ if (p != NULL)
+ addheader("Content-Transfer-Encoding", p, &ee->e_header);
+ }
+ if (strncmp(msg, "Warning:", 8) == 0)
+ {
+ addheader("Subject", msg, &ee->e_header);
+ p = "warning-timeout";
+ }
+ else if (strncmp(msg, "Postmaster warning:", 19) == 0)
+ {
+ addheader("Subject", msg, &ee->e_header);
+ p = "postmaster-warning";
+ }
+ else if (strcmp(msg, "Return receipt") == 0)
+ {
+ addheader("Subject", msg, &ee->e_header);
+ p = "return-receipt";
+ }
+ else if (bitset(RTSF_PM_BOUNCE, flags))
+ {
+ snprintf(buf, sizeof buf, "Postmaster notify: %.*s",
+ sizeof buf - 20, msg);
+ addheader("Subject", buf, &ee->e_header);
+ p = "postmaster-notification";
+ }
+ else
+ {
+ snprintf(buf, sizeof buf, "Returned mail: %.*s",
+ sizeof buf - 20, msg);
+ addheader("Subject", buf, &ee->e_header);
+ p = "failure";
+ }
+ (void) snprintf(buf, sizeof buf, "auto-generated (%s)", p);
+ addheader("Auto-Submitted", buf, &ee->e_header);
+
+ /* fake up an address header for the from person */
+ expand("\201n", buf, sizeof buf, e);
+ if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL)
+ {
+ syserr("553 Can't parse myself!");
+ ExitStat = EX_SOFTWARE;
+ returndepth--;
+ return (-1);
+ }
+ ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
+ ee->e_from.q_flags |= QPINGONFAILURE;
+ ee->e_sender = ee->e_from.q_paddr;
+
+ /* push state into submessage */
+ CurEnv = ee;
+ define('f', "\201n", ee);
+ define('x', "Mail Delivery Subsystem", ee);
+ eatheader(ee, TRUE);
+
+ /* mark statistics */
+ markstats(ee, NULLADDR, FALSE);
+
+ /* actually deliver the error message */
+ sendall(ee, SM_DELIVER);
+
+ /* restore state */
+ dropenvelope(ee, TRUE);
+ CurEnv = oldcur;
+ returndepth--;
+
+ /* check for delivery errors */
+ if (ee->e_parent == NULL || !bitset(EF_RESPONSE, ee->e_parent->e_flags))
+ return 0;
+ for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (bitset(QQUEUEUP|QSENT, q->q_flags))
+ return 0;
+ }
+ return -1;
+}
+ /*
+** ERRBODY -- output the body of an error message.
+**
+** Typically this is a copy of the transcript plus a copy of the
+** original offending message.
+**
+** Parameters:
+** mci -- the mailer connection information.
+** e -- the envelope we are working in.
+** separator -- any possible MIME separator.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Outputs the body of an error message.
+*/
+
+void
+errbody(mci, e, separator)
+ register MCI *mci;
+ register ENVELOPE *e;
+ char *separator;
+{
+ register FILE *xfile;
+ char *p;
+ register ADDRESS *q = NULL;
+ bool printheader;
+ bool sendbody;
+ bool pm_notify;
+ char buf[MAXLINE];
+
+ if (bitset(MCIF_INHEADER, mci->mci_flags))
+ {
+ putline("", mci);
+ mci->mci_flags &= ~MCIF_INHEADER;
+ }
+ if (e->e_parent == NULL)
+ {
+ syserr("errbody: null parent");
+ putline(" ----- Original message lost -----\n", mci);
+ return;
+ }
+
+ /*
+ ** Output MIME header.
+ */
+
+ if (e->e_msgboundary != NULL)
+ {
+ putline("This is a MIME-encapsulated message", mci);
+ putline("", mci);
+ (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary);
+ putline(buf, mci);
+ putline("", mci);
+ }
+
+ /*
+ ** Output introductory information.
+ */
+
+ pm_notify = FALSE;
+ p = hvalue("subject", e->e_header);
+ if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
+ pm_notify = TRUE;
+ else
+ {
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ if (bitset(QBADADDR, q->q_flags))
+ break;
+ }
+ if (!pm_notify && q == NULL &&
+ !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
+ {
+ putline(" **********************************************",
+ mci);
+ putline(" ** THIS IS A WARNING MESSAGE ONLY **",
+ mci);
+ putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **",
+ mci);
+ putline(" **********************************************",
+ mci);
+ putline("", mci);
+ }
+ snprintf(buf, sizeof buf, "The original message was received at %s",
+ arpadate(ctime(&e->e_parent->e_ctime)));
+ putline(buf, mci);
+ expand("from \201_", buf, sizeof buf, e->e_parent);
+ putline(buf, mci);
+ putline("", mci);
+
+ /*
+ ** Output error message header (if specified and available).
+ */
+
+ if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
+ {
+ if (*ErrMsgFile == '/')
+ {
+ int sff = SFF_ROOTOK|SFF_REGONLY;
+
+ if (DontLockReadFiles)
+ sff |= SFF_NOLOCK;
+ if (!bitset(DBS_ERRORHEADERINUNSAFEDIRPATH, DontBlameSendmail))
+ sff |= SFF_SAFEDIRPATH;
+ xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
+ if (xfile != NULL)
+ {
+ while (fgets(buf, sizeof buf, xfile) != NULL)
+ {
+ extern void translate_dollars __P((char *));
+
+ translate_dollars(buf);
+ expand(buf, buf, sizeof buf, e);
+ putline(buf, mci);
+ }
+ (void) fclose(xfile);
+ putline("\n", mci);
+ }
+ }
+ else
+ {
+ expand(ErrMsgFile, buf, sizeof buf, e);
+ putline(buf, mci);
+ putline("", mci);
+ }
+ }
+
+ /*
+ ** Output message introduction
+ */
+
+ printheader = TRUE;
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (!bitset(QBADADDR, q->q_flags) ||
+ !bitset(QPINGONFAILURE, q->q_flags))
+ continue;
+
+ if (printheader)
+ {
+ putline(" ----- The following addresses had permanent fatal errors -----",
+ mci);
+ printheader = FALSE;
+ }
+
+ snprintf(buf, sizeof buf, "%s",
+ shortenstring(q->q_paddr, MAXSHORTSTR));
+ putline(buf, mci);
+ if (q->q_alias != NULL)
+ {
+ snprintf(buf, sizeof buf, " (expanded from: %s)",
+ shortenstring(q->q_alias->q_paddr, MAXSHORTSTR));
+ putline(buf, mci);
+ }
+ }
+ if (!printheader)
+ putline("", mci);
+
+ printheader = TRUE;
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (bitset(QBADADDR, q->q_flags) ||
+ !bitset(QPRIMARY, q->q_flags) ||
+ !bitset(QDELAYED, q->q_flags))
+ continue;
+
+ if (printheader)
+ {
+ putline(" ----- The following addresses had transient non-fatal errors -----",
+ mci);
+ printheader = FALSE;
+ }
+
+ snprintf(buf, sizeof buf, "%s",
+ shortenstring(q->q_paddr, MAXSHORTSTR));
+ putline(buf, mci);
+ if (q->q_alias != NULL)
+ {
+ snprintf(buf, sizeof buf, " (expanded from: %s)",
+ shortenstring(q->q_alias->q_paddr, MAXSHORTSTR));
+ putline(buf, mci);
+ }
+ }
+ if (!printheader)
+ putline("", mci);
+
+ printheader = TRUE;
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (bitset(QBADADDR, q->q_flags) ||
+ !bitset(QPRIMARY, q->q_flags) ||
+ bitset(QDELAYED, q->q_flags))
+ continue;
+ else if (!bitset(QPINGONSUCCESS, q->q_flags))
+ continue;
+ else if (bitset(QRELAYED, q->q_flags))
+ p = "relayed to non-DSN-aware mailer";
+ else if (bitset(QDELIVERED, q->q_flags))
+ {
+ if (bitset(QEXPANDED, q->q_flags))
+ p = "successfully delivered to mailing list";
+ else
+ p = "successfully delivered to mailbox";
+ }
+ else if (bitset(QEXPANDED, q->q_flags))
+ p = "expanded by alias";
+ else
+ continue;
+
+ if (printheader)
+ {
+ putline(" ----- The following addresses had successful delivery notifications -----",
+ mci);
+ printheader = FALSE;
+ }
+
+ snprintf(buf, sizeof buf, "%s (%s)",
+ shortenstring(q->q_paddr, MAXSHORTSTR), p);
+ putline(buf, mci);
+ if (q->q_alias != NULL)
+ {
+ snprintf(buf, sizeof buf, " (expanded from: %s)",
+ shortenstring(q->q_alias->q_paddr, MAXSHORTSTR));
+ putline(buf, mci);
+ }
+ }
+ if (!printheader)
+ putline("", mci);
+
+ /*
+ ** Output transcript of errors
+ */
+
+ (void) fflush(stdout);
+ p = queuename(e->e_parent, 'x');
+ if ((xfile = fopen(p, "r")) == NULL)
+ {
+ syserr("Cannot open %s", p);
+ putline(" ----- Transcript of session is unavailable -----\n", mci);
+ }
+ else
+ {
+ printheader = TRUE;
+ if (e->e_xfp != NULL)
+ (void) fflush(e->e_xfp);
+ while (fgets(buf, sizeof buf, xfile) != NULL)
+ {
+ if (printheader)
+ putline(" ----- Transcript of session follows -----\n", mci);
+ printheader = FALSE;
+ putline(buf, mci);
+ }
+ (void) xfclose(xfile, "errbody xscript", p);
+ }
+ errno = 0;
+
+#if DSN
+ /*
+ ** Output machine-readable version.
+ */
+
+ if (e->e_msgboundary != NULL)
+ {
+ putline("", mci);
+ (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary);
+ putline(buf, mci);
+ putline("Content-Type: message/delivery-status", mci);
+ putline("", mci);
+
+ /*
+ ** Output per-message information.
+ */
+
+ /* original envelope id from MAIL FROM: line */
+ if (e->e_parent->e_envid != NULL)
+ {
+ (void) snprintf(buf, sizeof buf, "Original-Envelope-Id: %.800s",
+ xuntextify(e->e_parent->e_envid));
+ putline(buf, mci);
+ }
+
+ /* Reporting-MTA: is us (required) */
+ (void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName);
+ putline(buf, mci);
+
+ /* DSN-Gateway: not relevant since we are not translating */
+
+ /* Received-From-MTA: shows where we got this message from */
+ if (RealHostName != NULL)
+ {
+ /* XXX use $s for type? */
+ if (e->e_parent->e_from.q_mailer == NULL ||
+ (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
+ p = "dns";
+ (void) snprintf(buf, sizeof buf, "Received-From-MTA: %s; %.800s",
+ p, RealHostName);
+ putline(buf, mci);
+ }
+
+ /* Arrival-Date: -- when it arrived here */
+ (void) snprintf(buf, sizeof buf, "Arrival-Date: %s",
+ arpadate(ctime(&e->e_parent->e_ctime)));
+ putline(buf, mci);
+
+ /*
+ ** Output per-address information.
+ */
+
+ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ register ADDRESS *r;
+ char *action;
+
+ if (bitset(QBADADDR, q->q_flags))
+ action = "failed";
+ else if (!bitset(QPRIMARY, q->q_flags))
+ continue;
+ else if (bitset(QDELIVERED, q->q_flags))
+ {
+ if (bitset(QEXPANDED, q->q_flags))
+ action = "delivered (to mailing list)";
+ else
+ action = "delivered (to mailbox)";
+ }
+ else if (bitset(QRELAYED, q->q_flags))
+ action = "relayed (to non-DSN-aware mailer)";
+ else if (bitset(QEXPANDED, q->q_flags))
+ action = "expanded (to multi-recipient alias)";
+ else if (bitset(QDELAYED, q->q_flags))
+ action = "delayed";
+ else
+ continue;
+
+ putline("", mci);
+
+ /* Original-Recipient: -- passed from on high */
+ if (q->q_orcpt != NULL)
+ {
+ (void) snprintf(buf, sizeof buf, "Original-Recipient: %.800s",
+ q->q_orcpt);
+ putline(buf, mci);
+ }
+
+ /* Final-Recipient: -- the name from the RCPT command */
+ p = e->e_parent->e_from.q_mailer->m_addrtype;
+ if (p == NULL)
+ p = "rfc822";
+ for (r = q; r->q_alias != NULL; r = r->q_alias)
+ continue;
+ if (strchr(r->q_user, '@') != NULL)
+ {
+ (void) snprintf(buf, sizeof buf,
+ "Final-Recipient: %s; %.800s",
+ p, r->q_user);
+ }
+ else if (strchr(r->q_paddr, '@') != NULL)
+ {
+ (void) snprintf(buf, sizeof buf,
+ "Final-Recipient: %s; %.800s",
+ p, r->q_paddr);
+ }
+ else
+ {
+ (void) snprintf(buf, sizeof buf,
+ "Final-Recipient: %s; %.700s@%.100s",
+ p, r->q_user, MyHostName);
+ }
+ putline(buf, mci);
+
+ /* X-Actual-Recipient: -- the real problem address */
+ if (r != q && q->q_user[0] != '\0')
+ {
+ if (strchr(q->q_user, '@') == NULL)
+ {
+ (void) snprintf(buf, sizeof buf,
+ "X-Actual-Recipient: %s; %.700s@%.100s",
+ p, q->q_user, MyHostName);
+ }
+ else
+ {
+ (void) snprintf(buf, sizeof buf,
+ "X-Actual-Recipient: %s; %.800s",
+ p, q->q_user);
+ }
+ putline(buf, mci);
+ }
+
+ /* Action: -- what happened? */
+ snprintf(buf, sizeof buf, "Action: %s", action);
+ putline(buf, mci);
+
+ /* Status: -- what _really_ happened? */
+ if (q->q_status != NULL)
+ p = q->q_status;
+ else if (bitset(QBADADDR, q->q_flags))
+ p = "5.0.0";
+ else if (bitset(QQUEUEUP, q->q_flags))
+ p = "4.0.0";
+ else
+ p = "2.0.0";
+ snprintf(buf, sizeof buf, "Status: %s", p);
+ putline(buf, mci);
+
+ /* Remote-MTA: -- who was I talking to? */
+ if (q->q_statmta != NULL)
+ {
+ if (q->q_mailer == NULL ||
+ (p = q->q_mailer->m_mtatype) == NULL)
+ p = "dns";
+ (void) snprintf(buf, sizeof buf,
+ "Remote-MTA: %s; %.800s",
+ p, q->q_statmta);
+ p = &buf[strlen(buf) - 1];
+ if (*p == '.')
+ *p = '\0';
+ putline(buf, mci);
+ }
+
+ /* Diagnostic-Code: -- actual result from other end */
+ if (q->q_rstatus != NULL)
+ {
+ p = q->q_mailer->m_diagtype;
+ if (p == NULL)
+ p = "smtp";
+ (void) snprintf(buf, sizeof buf,
+ "Diagnostic-Code: %s; %.800s",
+ p, q->q_rstatus);
+ putline(buf, mci);
+ }
+
+ /* Last-Attempt-Date: -- fine granularity */
+ if (q->q_statdate == (time_t) 0L)
+ q->q_statdate = curtime();
+ (void) snprintf(buf, sizeof buf,
+ "Last-Attempt-Date: %s",
+ arpadate(ctime(&q->q_statdate)));
+ putline(buf, mci);
+
+ /* Will-Retry-Until: -- for delayed messages only */
+ if (bitset(QQUEUEUP, q->q_flags) &&
+ !bitset(QBADADDR, q->q_flags))
+ {
+ time_t xdate;
+
+ xdate = e->e_parent->e_ctime +
+ TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
+ snprintf(buf, sizeof buf,
+ "Will-Retry-Until: %s",
+ arpadate(ctime(&xdate)));
+ putline(buf, mci);
+ }
+ }
+ }
+#endif
+
+ /*
+ ** Output text of original message
+ */
+
+ putline("", mci);
+ if (bitset(EF_HAS_DF, e->e_parent->e_flags))
+ {
+ sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
+ !bitset(EF_NO_BODY_RETN, e->e_flags);
+
+ if (e->e_msgboundary == NULL)
+ {
+ if (sendbody)
+ putline(" ----- Original message follows -----\n", mci);
+ else
+ putline(" ----- Message header follows -----\n", mci);
+ (void) fflush(mci->mci_out);
+ }
+ else
+ {
+ (void) snprintf(buf, sizeof buf, "--%s",
+ e->e_msgboundary);
+
+ putline(buf, mci);
+ (void) snprintf(buf, sizeof buf, "Content-Type: %s",
+ sendbody ? "message/rfc822"
+ : "text/rfc822-headers");
+ putline(buf, mci);
+
+ p = hvalue("Content-Transfer-Encoding", e->e_parent->e_header);
+ if (p != NULL && strcasecmp(p, "binary") != 0)
+ p = NULL;
+ if (p == NULL && bitset(EF_HAS8BIT, e->e_parent->e_flags))
+ p = "8bit";
+ if (p != NULL)
+ {
+ (void) snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s",
+ p);
+ putline(buf, mci);
+ }
+ }
+ putline("", mci);
+ putheader(mci, e->e_parent->e_header, e->e_parent);
+ if (sendbody)
+ putbody(mci, e->e_parent, e->e_msgboundary);
+ else if (e->e_msgboundary == NULL)
+ {
+ putline("", mci);
+ putline(" ----- Message body suppressed -----", mci);
+ }
+ }
+ else if (e->e_msgboundary == NULL)
+ {
+ putline(" ----- No message was collected -----\n", mci);
+ }
+
+ if (e->e_msgboundary != NULL)
+ {
+ putline("", mci);
+ (void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary);
+ putline(buf, mci);
+ }
+ putline("", mci);
+
+ /*
+ ** Cleanup and exit
+ */
+
+ if (errno != 0)
+ syserr("errbody: I/O error");
+}
+ /*
+** SMTPTODSN -- convert SMTP to DSN status code
+**
+** Parameters:
+** smtpstat -- the smtp status code (e.g., 550).
+**
+** Returns:
+** The DSN version of the status code.
+*/
+
+char *
+smtptodsn(smtpstat)
+ int smtpstat;
+{
+ if (smtpstat < 0)
+ return "4.4.2";
+
+ switch (smtpstat)
+ {
+ case 450: /* Req mail action not taken: mailbox unavailable */
+ return "4.2.0";
+
+ case 451: /* Req action aborted: local error in processing */
+ return "4.3.0";
+
+ case 452: /* Req action not taken: insufficient sys storage */
+ return "4.3.1";
+
+ case 500: /* Syntax error, command unrecognized */
+ return "5.5.2";
+
+ case 501: /* Syntax error in parameters or arguments */
+ return "5.5.4";
+
+ case 502: /* Command not implemented */
+ return "5.5.1";
+
+ case 503: /* Bad sequence of commands */
+ return "5.5.1";
+
+ case 504: /* Command parameter not implemented */
+ return "5.5.4";
+
+ case 550: /* Req mail action not taken: mailbox unavailable */
+ return "5.2.0";
+
+ case 551: /* User not local; please try <...> */
+ return "5.1.6";
+
+ case 552: /* Req mail action aborted: exceeded storage alloc */
+ return "5.2.2";
+
+ case 553: /* Req action not taken: mailbox name not allowed */
+ return "5.1.0";
+
+ case 554: /* Transaction failed */
+ return "5.0.0";
+ }
+
+ if ((smtpstat / 100) == 2)
+ return "2.0.0";
+ if ((smtpstat / 100) == 4)
+ return "4.0.0";
+ return "5.0.0";
+}
+ /*
+** XTEXTIFY -- take regular text and turn it into DSN-style xtext
+**
+** Parameters:
+** t -- the text to convert.
+** taboo -- additional characters that must be encoded.
+**
+** Returns:
+** The xtext-ified version of the same string.
+*/
+
+char *
+xtextify(t, taboo)
+ register char *t;
+ char *taboo;
+{
+ register char *p;
+ int l;
+ int nbogus;
+ static char *bp = NULL;
+ static int bplen = 0;
+
+ if (taboo == NULL)
+ taboo = "";
+
+ /* figure out how long this xtext will have to be */
+ nbogus = l = 0;
+ for (p = t; *p != '\0'; p++)
+ {
+ register int c = (*p & 0xff);
+
+ /* ASCII dependence here -- this is the way the spec words it */
+ if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
+ strchr(taboo, c) != NULL)
+ nbogus++;
+ l++;
+ }
+ if (nbogus == 0)
+ return t;
+ l += nbogus * 2 + 1;
+
+ /* now allocate space if necessary for the new string */
+ if (l > bplen)
+ {
+ if (bp != NULL)
+ free(bp);
+ bp = xalloc(l);
+ bplen = l;
+ }
+
+ /* ok, copy the text with byte expansion */
+ for (p = bp; *t != '\0'; )
+ {
+ register int c = (*t++ & 0xff);
+
+ /* ASCII dependence here -- this is the way the spec words it */
+ if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
+ strchr(taboo, c) != NULL)
+ {
+ *p++ = '+';
+ *p++ = "0123456789abcdef"[c >> 4];
+ *p++ = "0123456789abcdef"[c & 0xf];
+ }
+ else
+ *p++ = c;
+ }
+ *p = '\0';
+ return bp;
+}
+ /*
+** XUNTEXTIFY -- take xtext and turn it into plain text
+**
+** Parameters:
+** t -- the xtextified text.
+**
+** Returns:
+** The decoded text. No attempt is made to deal with
+** null strings in the resulting text.
+*/
+
+char *
+xuntextify(t)
+ register char *t;
+{
+ register char *p;
+ int l;
+ static char *bp = NULL;
+ static int bplen = 0;
+
+ /* heuristic -- if no plus sign, just return the input */
+ if (strchr(t, '+') == NULL)
+ return t;
+
+ /* xtext is always longer than decoded text */
+ l = strlen(t);
+ if (l > bplen)
+ {
+ if (bp != NULL)
+ free(bp);
+ bp = xalloc(l);
+ bplen = l;
+ }
+
+ /* ok, copy the text with byte compression */
+ for (p = bp; *t != '\0'; t++)
+ {
+ register int c = *t & 0xff;
+
+ if (c != '+')
+ {
+ *p++ = c;
+ continue;
+ }
+
+ c = *++t & 0xff;
+ if (!isascii(c) || !isxdigit(c))
+ {
+ /* error -- first digit is not hex */
+ usrerr("bogus xtext: +%c", c);
+ t--;
+ continue;
+ }
+ if (isdigit(c))
+ c -= '0';
+ else if (isupper(c))
+ c -= 'A' - 10;
+ else
+ c -= 'a' - 10;
+ *p = c << 4;
+
+ c = *++t & 0xff;
+ if (!isascii(c) || !isxdigit(c))
+ {
+ /* error -- second digit is not hex */
+ usrerr("bogus xtext: +%x%c", *p >> 4, c);
+ t--;
+ continue;
+ }
+ if (isdigit(c))
+ c -= '0';
+ else if (isupper(c))
+ c -= 'A' - 10;
+ else
+ c -= 'a' - 10;
+ *p++ |= c;
+ }
+ *p = '\0';
+ return bp;
+}
+ /*
+** XTEXTOK -- check if a string is legal xtext
+**
+** Xtext is used in Delivery Status Notifications. The spec was
+** taken from RFC 1891, ``SMTP Service Extension for Delivery
+** Status Notifications''.
+**
+** Parameters:
+** s -- the string to check.
+**
+** Returns:
+** TRUE -- if 's' is legal xtext.
+** FALSE -- if it has any illegal characters in it.
+*/
+
+bool
+xtextok(s)
+ char *s;
+{
+ int c;
+
+ while ((c = *s++) != '\0')
+ {
+ if (c == '+')
+ {
+ c = *s++;
+ if (!isascii(c) || !isxdigit(c))
+ return FALSE;
+ c = *s++;
+ if (!isascii(c) || !isxdigit(c))
+ return FALSE;
+ }
+ else if (c < '!' || c > '~' || c == '=')
+ return FALSE;
+ }
+ return TRUE;
+}
+ /*
+** PRUNEROUTE -- prune an RFC-822 source route
+**
+** Trims down a source route to the last internet-registered hop.
+** This is encouraged by RFC 1123 section 5.3.3.
+**
+** Parameters:
+** addr -- the address
+**
+** Returns:
+** TRUE -- address was modified
+** FALSE -- address could not be pruned
+**
+** Side Effects:
+** modifies addr in-place
+*/
+
+bool
+pruneroute(addr)
+ char *addr;
+{
+#if NAMED_BIND
+ char *start, *at, *comma;
+ char c;
+ int rcode;
+ int i;
+ char hostbuf[BUFSIZ];
+ char *mxhosts[MAXMXHOSTS + 1];
+
+ /* check to see if this is really a route-addr */
+ if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
+ return FALSE;
+ start = strchr(addr, ':');
+ at = strrchr(addr, '@');
+ if (start == NULL || at == NULL || at < start)
+ return FALSE;
+
+ /* slice off the angle brackets */
+ i = strlen(at + 1);
+ if (i >= (SIZE_T) sizeof hostbuf)
+ return FALSE;
+ strcpy(hostbuf, at + 1);
+ hostbuf[i - 1] = '\0';
+
+ while (start)
+ {
+ if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0)
+ {
+ strcpy(addr + 1, start + 1);
+ return TRUE;
+ }
+ c = *start;
+ *start = '\0';
+ comma = strrchr(addr, ',');
+ if (comma != NULL && comma[1] == '@' &&
+ strlen(comma + 2) < (SIZE_T) sizeof hostbuf)
+ strcpy(hostbuf, comma + 2);
+ else
+ comma = NULL;
+ *start = c;
+ start = comma;
+ }
+#endif
+ return FALSE;
+}
diff --git a/contrib/sendmail/src/sendmail.8 b/contrib/sendmail/src/sendmail.8
new file mode 100644
index 0000000..5105e83
--- /dev/null
+++ b/contrib/sendmail/src/sendmail.8
@@ -0,0 +1,577 @@
+.\" Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+.\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved.
+.\" Copyright (c) 1988, 1991, 1993
+.\" The Regents of the University of California. 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.
+.\"
+.\"
+.\" @(#)sendmail.8 8.19 (Berkeley) 5/19/98
+.\"
+.Dd May 19, 1998
+.Dt SENDMAIL 8
+.Os BSD 4
+.Sh NAME
+.Nm sendmail
+.Nd an electronic mail transport agent
+.Sh SYNOPSIS
+.Nm sendmail
+.Op Ar flags
+.Op Ar address ...
+.Nm newaliases
+.Nm mailq
+.Op Fl v
+.Sh DESCRIPTION
+.Nm Sendmail
+sends a message to one or more
+.Em recipients ,
+routing the message over whatever networks
+are necessary.
+.Nm Sendmail
+does internetwork forwarding as necessary
+to deliver the message to the correct place.
+.Pp
+.Nm Sendmail
+is not intended as a user interface routine;
+other programs provide user-friendly
+front ends;
+.Nm sendmail
+is used only to deliver pre-formatted messages.
+.Pp
+With no flags,
+.Nm sendmail
+reads its standard input
+up to an end-of-file
+or a line consisting only of a single dot
+and sends a copy of the message found there
+to all of the addresses listed.
+It determines the network(s) to use
+based on the syntax and contents of the addresses.
+.Pp
+Local addresses are looked up in a file
+and aliased appropriately.
+Aliasing can be prevented by preceding the address
+with a backslash.
+Normally the sender is not included in any alias
+expansions, e.g.,
+if `john' sends to `group',
+and `group' includes `john' in the expansion,
+then the letter will not be delivered to `john'.
+.Ss Parameters
+.Bl -tag -width Fl
+.It Fl B Ns Ar type
+Set the body type to
+.Ar type .
+Current legal values
+.Li 7BIT
+or
+.Li 8BITMIME .
+.It Fl ba
+Go into
+.Tn ARPANET
+mode.
+All input lines must end with a CR-LF,
+and all messages will be generated with a CR-LF at the end.
+Also,
+the ``From:'' and ``Sender:''
+fields are examined for the name of the sender.
+.It Fl bd
+Run as a daemon. This requires Berkeley
+.Tn IPC .
+.Nm Sendmail
+will fork and run in background
+listening on socket 25 for incoming
+.Tn SMTP
+connections.
+This is normally run from
+.Pa /etc/rc .
+.It Fl bD
+Same as
+.Fl bd
+except runs in foreground.
+.It Fl bh
+Print the persistent host status database.
+.It Fl bH
+Purge the persistent host status database.
+.It Fl bi
+Initialize the alias database.
+.It Fl bm
+Deliver mail in the usual way (default).
+.It Fl bp
+Print a listing of the queue.
+.It Fl bs
+Use the
+.Tn SMTP
+protocol as described in
+.Tn RFC821
+on standard input and output.
+This flag implies all the operations of the
+.Fl ba
+flag that are compatible with
+.Tn SMTP .
+.It Fl bt
+Run in address test mode.
+This mode reads addresses and shows the steps in parsing;
+it is used for debugging configuration tables.
+.It Fl bv
+Verify names only \- do not try to collect or deliver a message.
+Verify mode is normally used for validating
+users or mailing lists.
+.It Fl C Ns Ar file
+Use alternate configuration file.
+.Nm Sendmail
+refuses to run as root if an alternate configuration file is specified.
+.It Fl d Ns Ar X
+Set debugging value to
+.Ar X .
+.ne 1i
+.It Fl F Ns Ar fullname
+Set the full name of the sender.
+.It Fl f Ns Ar name
+Sets the name of the ``from'' person
+(i.e., the sender of the mail).
+.Fl f
+can only be used
+by ``trusted'' users
+(normally
+.Em root ,
+.Em daemon ,
+and
+.Em network )
+or if the person you are trying to become
+is the same as the person you are.
+.It Fl h Ns Ar N
+Set the hop count to
+.Ar N .
+The hop count is incremented every time the mail is
+processed.
+When it reaches a limit,
+the mail is returned with an error message,
+the victim of an aliasing loop.
+If not specified,
+``Received:'' lines in the message are counted.
+.It Fl i
+Ignore dots alone on lines by themselves in incoming messages.
+This should be set if you are reading data from a file.
+.It Fl N Ar dsn
+Set delivery status notification conditions to
+.Ar dsn,
+which can be
+.Ql never
+for no notifications
+or a comma separated list of the values
+.Ql failure
+to be notified if delivery failed,
+.Ql delay
+to be notified if delivery is delayed, and
+.Ql success
+to be notified when the message is successfully delivered.
+.It Fl n
+Don't do aliasing.
+.It Fl O Ar option Ns = Ns Em value
+Set option
+.Ar option
+to the specified
+.Em value .
+This form uses long names.
+See below for more details.
+.It Fl o Ns Ar x Em value
+Set option
+.Ar x
+to the specified
+.Em value .
+This form uses single character names only.
+The short names are not described in this manual page;
+see the
+.%T "Sendmail Installation and Operation Guide"
+for details.
+.It Fl p Ns Ar protocol
+Set the name of the protocol used to receive the message.
+This can be a simple protocol name such as ``UUCP''
+or a protocol and hostname, such as ``UUCP:ucbvax''.
+.It Fl q Ns Bq Ar time
+Processed saved messages in the queue at given intervals.
+If
+.Ar time
+is omitted,
+process the queue once.
+.Xr Time
+is given as a tagged number,
+with
+.Ql s
+being seconds,
+.Ql m
+being minutes,
+.Ql h
+being hours,
+.Ql d
+being days,
+and
+.Ql w
+being weeks.
+For example,
+.Ql \-q1h30m
+or
+.Ql \-q90m
+would both set the timeout to one hour thirty minutes.
+If
+.Ar time
+is specified,
+.Nm sendmail
+will run in background.
+This option can be used safely with
+.Fl bd .
+.It Fl qI Ns Ar substr
+Limit processed jobs to those containing
+.Ar substr
+as a substring of the queue id.
+.It Fl qR Ns Ar substr
+Limit processed jobs to those containing
+.Ar substr
+as a substring of one of the recipients.
+.It Fl qS Ns Ar substr
+Limit processed jobs to those containing
+.Ar substr
+as a substring of the sender.
+.It Fl R Ar return
+Set the amount of the message to be returned
+if the message bounces.
+The
+.Ar return
+parameter can be
+.Ql full
+to return the entire message or
+.Ql hdrs
+to return only the headers.
+.It Fl r Ns Ar name
+An alternate and obsolete form of the
+.Fl f
+flag.
+.It Fl t
+Read message for recipients.
+To:, Cc:, and Bcc: lines will be scanned for recipient addresses.
+The Bcc: line will be deleted before transmission.
+.It Fl U
+Initial (user) submission.
+This should
+.Em always
+be set when called from a user agent such as
+.Nm Mail
+or
+.Nm exmh
+and
+.Em never
+be set when called by a network delivery agent such as
+.Nm rmail .
+.It Fl V Ar envid
+Set the original envelope id.
+This is propagated across SMTP to servers that support DSNs
+and is returned in DSN-compliant error messages.
+.It Fl v
+Go into verbose mode.
+Alias expansions will be announced, etc.
+.It Fl X Ar logfile
+Log all traffic in and out of mailers in the indicated log file.
+This should only be used as a last resort
+for debugging mailer bugs.
+It will log a lot of data very quickly.
+.El
+.Ss Options
+There are also a number of processing options that may be set.
+Normally these will only be used by a system administrator.
+Options may be set either on the command line
+using the
+.Fl o
+flag (for short names),
+the
+.Fl O
+flag (for long names),
+or in the configuration file.
+This is a partial list limited to those options that are likely to be useful
+on the command line
+and only shows the long names;
+for a complete list (and details), consult the
+.%T "Sendmail Installation and Operation Guide" .
+The options are:
+.Bl -tag -width Fl
+.It Li AliasFile= Ns Ar file
+Use alternate alias file.
+.It Li HoldExpensive
+On mailers that are considered ``expensive'' to connect to,
+don't initiate immediate connection.
+This requires queueing.
+.It Li CheckpointInterval= Ns Ar N
+Checkpoint the queue file after every
+.Ar N
+successful deliveries (default 10).
+This avoids excessive duplicate deliveries
+when sending to long mailing lists
+interrupted by system crashes.
+.ne 1i
+.It Li DeliveryMode= Ns Ar x
+Set the delivery mode to
+.Ar x .
+Delivery modes are
+.Ql i
+for interactive (synchronous) delivery,
+.Ql b
+for background (asynchronous) delivery,
+.Ql q
+for queue only \- i.e.,
+actual delivery is done the next time the queue is run, and
+.Ql d
+for deferred \- the same as
+.Ql q
+except that database lookups (notably DNS and NIS lookups) are avoided.
+.It Li ErrorMode= Ns Ar x
+Set error processing to mode
+.Ar x .
+Valid modes are
+.Ql m
+to mail back the error message,
+.Ql w
+to ``write'' back the error message
+(or mail it back if the sender is not logged in),
+.Ql p
+to print the errors on the terminal
+(default),
+.Ql q
+to throw away error messages
+(only exit status is returned),
+and
+.Ql e
+to do special processing for the BerkNet.
+If the text of the message is not mailed back
+by
+modes
+.Ql m
+or
+.Ql w
+and if the sender is local to this machine,
+a copy of the message is appended to the file
+.Pa dead.letter
+in the sender's home directory.
+.It Li SaveFromLine
+Save
+.Tn UNIX Ns \-style
+From lines at the front of messages.
+.It Li MaxHopCount= Ar N
+The maximum number of times a message is allowed to ``hop''
+before we decide it is in a loop.
+.It Li IgnoreDots
+Do not take dots on a line by themselves
+as a message terminator.
+.It Li SendMimeErrors
+Send error messages in MIME format.
+If not set, the DSN (Delivery Status Notification) SMTP extension
+is disabled.
+.It Li ConnectionCacheTimeout= Ns Ar timeout
+Set connection cache timeout.
+.It Li ConnectionCacheSize= Ns Ar N
+Set connection cache size.
+.It Li LogLevel= Ns Ar n
+The log level.
+.It Li MeToo
+Send to ``me'' (the sender) also if I am in an alias expansion.
+.It Li CheckAliases
+Validate the right hand side of aliases during a
+.Xr newaliases 1
+command.
+.It Li OldStyleHeaders
+If set, this message may have
+old style headers.
+If not set,
+this message is guaranteed to have new style headers
+(i.e., commas instead of spaces between addresses).
+If set, an adaptive algorithm is used that will correctly
+determine the header format in most cases.
+.It Li QueueDirectory= Ns Ar queuedir
+Select the directory in which to queue messages.
+.It Li StatusFile= Ns Ar file
+Save statistics in the named file.
+.It Li Timeout.queuereturn= Ns Ar time
+Set the timeout on undelivered messages in the queue to the specified time.
+After delivery has failed
+(e.g., because of a host being down)
+for this amount of time,
+failed messages will be returned to the sender.
+The default is five days.
+.It Li UserDatabaseSpec= Ns Ar userdatabase
+If set, a user database is consulted to get forwarding information.
+You can consider this an adjunct to the aliasing mechanism,
+except that the database is intended to be distributed;
+aliases are local to a particular host.
+This may not be available if your sendmail does not have the
+.Dv USERDB
+option compiled in.
+.It Li ForkEachJob
+Fork each job during queue runs.
+May be convenient on memory-poor machines.
+.It Li SevenBitInput
+Strip incoming messages to seven bits.
+.It Li EightBitMode= Ns Ar mode
+Set the handling of eight bit input to seven bit destinations to
+.Ar mode :
+.Li m
+(mimefy) will convert to seven-bit MIME format,
+.Li p
+(pass) will pass it as eight bits (but violates protocols),
+and
+.Li s
+(strict) will bounce the message.
+.It Li MinQueueAge= Ns Ar timeout
+Sets how long a job must ferment in the queue between attempts to send it.
+.It Li DefaultCharSet= Ns Ar charset
+Sets the default character set used to label 8-bit data
+that is not otherwise labelled.
+.It Li DialDelay= Ns Ar sleeptime
+If opening a connection fails,
+sleep for
+.Ar sleeptime
+seconds and try again.
+Useful on dial-on-demand sites.
+.It Li NoRecipientAction= Ns Ar action
+Set the behaviour when there are no recipient headers (To:, Cc: or Bcc:)
+in the message to
+.Ar action :
+.Li none
+leaves the message unchanged,
+.Li add-to
+adds a To: header with the envelope recipients,
+.Li add-apparently-to
+adds an Apparently-To: header with the envelope recipients,
+.Li add-bcc
+adds an empty Bcc: header, and
+.Li add-to-undisclosed
+adds a header reading
+.Ql "To: undisclosed-recipients:;" .
+.It Li MaxDaemonChildren= Ns Ar N
+Sets the maximum number of children that an incoming SMTP daemon
+will allow to spawn at any time to
+.Ar N .
+.It Li ConnectionRateThrottle= Ns Ar N
+Sets the maximum number of connections per second to the SMTP port to
+.Ar N .
+.El
+.Pp
+In aliases,
+the first character of a name may be
+a vertical bar to cause interpretation of
+the rest of the name as a command
+to pipe the mail to.
+It may be necessary to quote the name
+to keep
+.Nm sendmail
+from suppressing the blanks from between arguments.
+For example, a common alias is:
+.Pp
+.Bd -literal -offset indent -compact
+msgs: "|/usr/bin/msgs -s"
+.Ed
+.Pp
+Aliases may also have the syntax
+.Dq :include: Ns Ar filename
+to ask
+.Xr sendmail
+to read the named file for a list of recipients.
+For example, an alias such as:
+.Pp
+.Bd -literal -offset indent -compact
+poets: ":include:/usr/local/lib/poets.list"
+.Ed
+.Pp
+would read
+.Pa /usr/local/lib/poets.list
+for the list of addresses making up the group.
+.Pp
+.Nm Sendmail
+returns an exit status
+describing what it did.
+The codes are defined in
+.Aq Pa sysexits.h :
+.Bl -tag -width EX_UNAVAILABLE -compact -offset indent
+.It Dv EX_OK
+Successful completion on all addresses.
+.It Dv EX_NOUSER
+User name not recognized.
+.It Dv EX_UNAVAILABLE
+Catchall meaning necessary resources
+were not available.
+.It Dv EX_SYNTAX
+Syntax error in address.
+.It Dv EX_SOFTWARE
+Internal software error,
+including bad arguments.
+.It Dv EX_OSERR
+Temporary operating system error,
+such as
+.Dq cannot fork .
+.It Dv EX_NOHOST
+Host name not recognized.
+.It Dv EX_TEMPFAIL
+Message could not be sent immediately,
+but was queued.
+.El
+.Pp
+If invoked as
+.Nm newaliases ,
+.Nm sendmail
+will rebuild the alias database.
+If invoked as
+.Nm mailq ,
+.Nm sendmail
+will print the contents of the mail queue.
+.Sh FILES
+Except for the file
+.Pa /etc/sendmail.cf
+itself and the daemon process ID file,
+the following pathnames are all specified in
+.Pa /etc/sendmail.cf.
+Thus,
+these values are only approximations.
+.Pp
+.Bl -tag -width /usr/lib/sendmail.fc -compact
+.It Pa /etc/aliases
+raw data for alias names
+.It Pa /etc/aliases.db
+data base of alias names
+.It Pa /etc/sendmail.cf
+configuration file
+.It Pa /etc/sendmail.hf
+help file
+.It Pa /var/log/sendmail.st
+collected statistics
+.It Pa /var/spool/mqueue/*
+temp files
+.El
+.Sh SEE ALSO
+.Xr binmail 1 ,
+.Xr mail 1 ,
+.Xr rmail 1 ,
+.Xr syslog 3 ,
+.Xr aliases 5 ,
+.Xr mailaddr 7 ,
+.Xr rc 8 ;
+.Pp
+DARPA
+Internet Request For Comments
+.%T RFC819 ,
+.%T RFC821 ,
+.%T RFC822 .
+.Rs
+.%T "Sendmail \- An Internetwork Mail Router"
+.%V SMM
+.%N \&No. 9
+.Re
+.Rs
+.%T "Sendmail Installation and Operation Guide"
+.%V SMM
+.%N \&No. 8
+.Re
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/contrib/sendmail/src/sendmail.h b/contrib/sendmail/src/sendmail.h
new file mode 100644
index 0000000..7bfe11b
--- /dev/null
+++ b/contrib/sendmail/src/sendmail.h
@@ -0,0 +1,1500 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ *
+ * @(#)sendmail.h 8.280 (Berkeley) 6/5/98
+ */
+
+/*
+** SENDMAIL.H -- Global definitions for sendmail.
+*/
+
+# ifdef _DEFINE
+# define EXTERN
+# ifndef lint
+static char SmailSccsId[] = "@(#)sendmail.h 8.280 6/5/98";
+# endif
+# else /* _DEFINE */
+# define EXTERN extern
+# endif /* _DEFINE */
+
+# include <unistd.h>
+# include <stddef.h>
+# include <stdlib.h>
+# include <stdio.h>
+# include <ctype.h>
+# include <setjmp.h>
+# include <string.h>
+# include <time.h>
+# include <errno.h>
+# ifdef EX_OK
+# undef EX_OK /* for SVr4.2 SMP */
+# endif
+# include <sysexits.h>
+
+# include "conf.h"
+# include "useful.h"
+
+# ifdef LOG
+# include <syslog.h>
+# endif /* LOG */
+
+# if NETINET || NETUNIX || NETISO || NETNS || NETX25
+# include <sys/socket.h>
+# endif
+# if NETUNIX
+# include <sys/un.h>
+# endif
+# if NETINET
+# include <netinet/in.h>
+# endif
+# if NETISO
+# include <netiso/iso.h>
+# endif
+# if NETNS
+# include <netns/ns.h>
+# endif
+# if NETX25
+# include <netccitt/x25.h>
+# endif
+
+#if NAMED_BIND
+# include <arpa/nameser.h>
+# ifdef NOERROR
+# undef NOERROR /* avoid <sys/streams.h> conflict */
+# endif
+#endif
+
+#ifdef HESIOD
+# include <hesiod.h>
+# if !defined(HES_ER_OK) || defined(HESIOD_INTERFACES)
+# define HESIOD_INIT /* support for the new interface */
+EXTERN void *HesiodContext;
+# endif
+#endif
+
+/*
+** Following are "sort of" configuration constants, but they should
+** be pretty solid on most architectures today. They have to be
+** defined after <arpa/nameser.h> because some versions of that
+** file also define them. In all cases, we can't use sizeof because
+** some systems (e.g., Crays) always treat everything as being at
+** least 64 bits.
+*/
+
+#ifndef INADDRSZ
+# define INADDRSZ 4 /* size of an IPv4 address in bytes */
+#endif
+#ifndef INT16SZ
+# define INT16SZ 2 /* size of a 16 bit integer in bytes */
+#endif
+#ifndef INT32SZ
+# define INT32SZ 4 /* size of a 32 bit integer in bytes */
+#endif
+
+
+
+/* forward references for prototypes */
+typedef struct envelope ENVELOPE;
+typedef struct mailer MAILER;
+
+
+/*
+** Data structure for bit maps.
+**
+** Each bit in this map can be referenced by an ascii character.
+** This is 256 possible bits, or 32 8-bit bytes.
+*/
+
+#define BITMAPBYTES 32 /* number of bytes in a bit map */
+#define BYTEBITS 8 /* number of bits in a byte */
+
+/* internal macros */
+#define _BITWORD(bit) ((bit) / (BYTEBITS * sizeof (int)))
+#define _BITBIT(bit) (1 << ((bit) % (BYTEBITS * sizeof (int))))
+
+typedef int BITMAP[BITMAPBYTES / sizeof (int)];
+
+/* test bit number N */
+#define bitnset(bit, map) ((map)[_BITWORD(bit)] & _BITBIT(bit))
+
+/* set bit number N */
+#define setbitn(bit, map) (map)[_BITWORD(bit)] |= _BITBIT(bit)
+
+/* clear bit number N */
+#define clrbitn(bit, map) (map)[_BITWORD(bit)] &= ~_BITBIT(bit)
+
+/* clear an entire bit map */
+#define clrbitmap(map) bzero((char *) map, BITMAPBYTES)
+
+
+/*
+** Utility macros
+*/
+
+/* return number of bytes left in a buffer */
+#define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf))
+ /*
+** Address structure.
+** Addresses are stored internally in this structure.
+*/
+
+struct address
+{
+ char *q_paddr; /* the printname for the address */
+ char *q_user; /* user name */
+ char *q_ruser; /* real user name, or NULL if q_user */
+ char *q_host; /* host name */
+ struct mailer *q_mailer; /* mailer to use */
+ u_long q_flags; /* status flags, see below */
+ uid_t q_uid; /* user-id of receiver (if known) */
+ gid_t q_gid; /* group-id of receiver (if known) */
+ char *q_home; /* home dir (local mailer only) */
+ char *q_fullname; /* full name if known */
+ struct address *q_next; /* chain */
+ struct address *q_alias; /* address this results from */
+ char *q_owner; /* owner of q_alias */
+ struct address *q_tchain; /* temporary use chain */
+ char *q_orcpt; /* ORCPT parameter from RCPT TO: line */
+ char *q_status; /* status code for DSNs */
+ char *q_rstatus; /* remote status message for DSNs */
+ time_t q_statdate; /* date of status messages */
+ char *q_statmta; /* MTA generating q_rstatus */
+ short q_specificity; /* how "specific" this address is */
+};
+
+typedef struct address ADDRESS;
+
+# define QDONTSEND 0x00000001 /* don't send to this address */
+# define QBADADDR 0x00000002 /* this address is verified bad */
+# define QGOODUID 0x00000004 /* the q_uid q_gid fields are good */
+# define QPRIMARY 0x00000008 /* set from RCPT or argv */
+# define QQUEUEUP 0x00000010 /* queue for later transmission */
+# define QSENT 0x00000020 /* has been successfully delivered */
+# define QNOTREMOTE 0x00000040 /* address not for remote forwarding */
+# define QSELFREF 0x00000080 /* this address references itself */
+# define QVERIFIED 0x00000100 /* verified, but not expanded */
+# define QBOGUSSHELL 0x00000400 /* user has no valid shell listed */
+# define QUNSAFEADDR 0x00000800 /* address aquired via unsafe path */
+# define QPINGONSUCCESS 0x00001000 /* give return on successful delivery */
+# define QPINGONFAILURE 0x00002000 /* give return on failure */
+# define QPINGONDELAY 0x00004000 /* give return on message delay */
+# define QHASNOTIFY 0x00008000 /* propogate notify parameter */
+# define QRELAYED 0x00010000 /* DSN: relayed to non-DSN aware sys */
+# define QEXPANDED 0x00020000 /* DSN: undergone list expansion */
+# define QDELIVERED 0x00040000 /* DSN: successful final delivery */
+# define QDELAYED 0x00080000 /* DSN: message delayed */
+# define QTHISPASS 0x40000000 /* temp: address set this pass */
+# define QRCPTOK 0x80000000 /* recipient() processed address */
+
+# define Q_PINGFLAGS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY)
+
+# define NULLADDR ((ADDRESS *) NULL)
+
+/* functions */
+extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **, ENVELOPE *));
+extern ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
+extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
+extern char **prescan __P((char *, int, char[], int, char **, u_char *));
+extern int rewrite __P((char **, int, int, ENVELOPE *));
+extern char *remotename __P((char *, MAILER *, int, int *, ENVELOPE *));
+extern ADDRESS *getctladdr __P((ADDRESS *));
+extern bool sameaddr __P((ADDRESS *, ADDRESS *));
+extern bool emptyaddr __P((ADDRESS *));
+extern void printaddr __P((ADDRESS *, bool));
+extern void cataddr __P((char **, char **, char *, int, int));
+extern int sendtolist __P((char *, ADDRESS *, ADDRESS **, int, ENVELOPE *));
+ /*
+** Mailer definition structure.
+** Every mailer known to the system is declared in this
+** structure. It defines the pathname of the mailer, some
+** flags associated with it, and the argument vector to
+** pass to it. The flags are defined in conf.c
+**
+** The argument vector is expanded before actual use. All
+** words except the first are passed through the macro
+** processor.
+*/
+
+struct mailer
+{
+ char *m_name; /* symbolic name of this mailer */
+ char *m_mailer; /* pathname of the mailer to use */
+ char *m_mtatype; /* type of this MTA */
+ char *m_addrtype; /* type for addresses */
+ char *m_diagtype; /* type for diagnostics */
+ BITMAP m_flags; /* status flags, see below */
+ short m_mno; /* mailer number internally */
+ short m_nice; /* niceness to run at (mostly for prog) */
+ char **m_argv; /* template argument vector */
+ short m_sh_rwset; /* rewrite set: sender header addresses */
+ short m_se_rwset; /* rewrite set: sender envelope addresses */
+ short m_rh_rwset; /* rewrite set: recipient header addresses */
+ short m_re_rwset; /* rewrite set: recipient envelope addresses */
+ char *m_eol; /* end of line string */
+ long m_maxsize; /* size limit on message to this mailer */
+ int m_linelimit; /* max # characters per line */
+ char *m_execdir; /* directory to chdir to before execv */
+ uid_t m_uid; /* UID to run as */
+ gid_t m_gid; /* GID to run as */
+ char *m_defcharset; /* default character set */
+};
+
+/* bits for m_flags */
+# define M_ESMTP 'a' /* run Extended SMTP protocol */
+# define M_ALIASABLE 'A' /* user can be LHS of an alias */
+# define M_BLANKEND 'b' /* ensure blank line at end of message */
+# define M_NOCOMMENT 'c' /* don't include comment part of address */
+# define M_CANONICAL 'C' /* make addresses canonical "u@dom" */
+# define M_NOBRACKET 'd' /* never angle bracket envelope route-addrs */
+ /* 'D' CF: include Date: */
+# define M_EXPENSIVE 'e' /* it costs to use this mailer.... */
+# define M_ESCFROM 'E' /* escape From lines to >From */
+# define M_FOPT 'f' /* mailer takes picky -f flag */
+ /* 'F' CF: include From: or Resent-From: */
+# define M_NO_NULL_FROM 'g' /* sender of errors should be $g */
+# define M_HST_UPPER 'h' /* preserve host case distinction */
+# define M_PREHEAD 'H' /* MAIL11V3: preview headers */
+# define M_UDBENVELOPE 'i' /* do udbsender rewriting on envelope */
+# define M_INTERNAL 'I' /* SMTP to another sendmail site */
+# define M_UDBRECIPIENT 'j' /* do udbsender rewriting on recipient lines */
+# define M_NOLOOPCHECK 'k' /* don't check for loops in HELO command */
+# define M_CHUNKING 'K' /* CHUNKING: reserved for future use */
+# define M_LOCALMAILER 'l' /* delivery is to this host */
+# define M_LIMITS 'L' /* must enforce SMTP line limits */
+# define M_MUSER 'm' /* can handle multiple users at once */
+ /* 'M' CF: include Message-Id: */
+# define M_NHDR 'n' /* don't insert From line */
+# define M_MANYSTATUS 'N' /* MAIL11V3: DATA returns multi-status */
+# define M_RUNASRCPT 'o' /* always run mailer as recipient */
+# define M_FROMPATH 'p' /* use reverse-path in MAIL FROM: */
+ /* 'P' CF: include Return-Path: */
+# define M_VRFY250 'q' /* VRFY command returns 250 instead of 252 */
+# define M_ROPT 'r' /* mailer takes picky -r flag */
+# define M_SECURE_PORT 'R' /* try to send on a reserved TCP port */
+# define M_STRIPQ 's' /* strip quote chars from user/host */
+# define M_SPECIFIC_UID 'S' /* run as specific uid/gid */
+# define M_USR_UPPER 'u' /* preserve user case distinction */
+# define M_UGLYUUCP 'U' /* this wants an ugly UUCP from line */
+# define M_CONTENT_LEN 'v' /* add Content-Length: header (SVr4) */
+ /* 'V' UIUC: !-relativize all addresses */
+# define M_HASPWENT 'w' /* check for /etc/passwd entry */
+ /* 'x' CF: include Full-Name: */
+# define M_XDOT 'X' /* use hidden-dot algorithm */
+# define M_LMTP 'z' /* run Local Mail Transport Protocol */
+# define M_NOMX '0' /* turn off MX lookups */
+# define M_NONULLS '1' /* don't send null bytes */
+# define M_EBCDIC '3' /* extend Q-P encoding for EBCDIC */
+# define M_TRYRULESET5 '5' /* use ruleset 5 after local aliasing */
+# define M_7BITHDRS '6' /* strip headers to 7 bits even in 8 bit path */
+# define M_7BITS '7' /* use 7-bit path */
+# define M_8BITS '8' /* force "just send 8" behaviour */
+# define M_MAKE8BIT '9' /* convert 7 -> 8 bit if appropriate */
+# define M_CHECKINCLUDE ':' /* check for :include: files */
+# define M_CHECKPROG '|' /* check for |program addresses */
+# define M_CHECKFILE '/' /* check for /file addresses */
+# define M_CHECKUDB '@' /* user can be user database key */
+# define M_CHECKHDIR '~' /* SGI: check for valid home directory */
+
+EXTERN MAILER *Mailer[MAXMAILERS+1];
+
+EXTERN MAILER *LocalMailer; /* ptr to local mailer */
+EXTERN MAILER *ProgMailer; /* ptr to program mailer */
+EXTERN MAILER *FileMailer; /* ptr to *file* mailer */
+EXTERN MAILER *InclMailer; /* ptr to *include* mailer */
+ /*
+** Information about currently open connections to mailers, or to
+** hosts that we have looked up recently.
+*/
+
+# define MCI struct mailer_con_info
+
+MCI
+{
+ short mci_flags; /* flag bits, see below */
+ short mci_errno; /* error number on last connection */
+ short mci_herrno; /* h_errno from last DNS lookup */
+ short mci_exitstat; /* exit status from last connection */
+ short mci_state; /* SMTP state */
+ off_t mci_contentlen; /* body length for Content-Length: */
+ long mci_maxsize; /* max size this server will accept */
+ FILE *mci_in; /* input side of connection */
+ FILE *mci_out; /* output side of connection */
+ pid_t mci_pid; /* process id of subordinate proc */
+ char *mci_phase; /* SMTP phase string */
+ struct mailer *mci_mailer; /* ptr to the mailer for this conn */
+ char *mci_host; /* host name */
+ char *mci_status; /* DSN status to be copied to addrs */
+ char *mci_rstatus; /* SMTP status to be copied to addrs */
+ time_t mci_lastuse; /* last usage time */
+ FILE *mci_statfile; /* long term status file */
+};
+
+
+/* flag bits */
+#define MCIF_VALID 0x0001 /* this entry is valid */
+#define MCIF_TEMP 0x0002 /* don't cache this connection */
+#define MCIF_CACHED 0x0004 /* currently in open cache */
+#define MCIF_ESMTP 0x0008 /* this host speaks ESMTP */
+#define MCIF_EXPN 0x0010 /* EXPN command supported */
+#define MCIF_SIZE 0x0020 /* SIZE option supported */
+#define MCIF_8BITMIME 0x0040 /* BODY=8BITMIME supported */
+#define MCIF_7BIT 0x0080 /* strip this message to 7 bits */
+#define MCIF_MULTSTAT 0x0100 /* MAIL11V3: handles MULT status */
+#define MCIF_INHEADER 0x0200 /* currently outputing header */
+#define MCIF_CVT8TO7 0x0400 /* convert from 8 to 7 bits */
+#define MCIF_DSN 0x0800 /* DSN extension supported */
+#define MCIF_8BITOK 0x1000 /* OK to send 8 bit characters */
+#define MCIF_CVT7TO8 0x2000 /* convert from 7 to 8 bits */
+#define MCIF_INMIME 0x4000 /* currently reading MIME header */
+
+/* states */
+#define MCIS_CLOSED 0 /* no traffic on this connection */
+#define MCIS_OPENING 1 /* sending initial protocol */
+#define MCIS_OPEN 2 /* open, initial protocol sent */
+#define MCIS_ACTIVE 3 /* message being sent */
+#define MCIS_QUITING 4 /* running quit protocol */
+#define MCIS_SSD 5 /* service shutting down */
+#define MCIS_ERROR 6 /* I/O error on connection */
+
+/* functions */
+extern MCI *mci_get __P((char *, MAILER *));
+extern void mci_cache __P((MCI *));
+extern void mci_flush __P((bool, MCI *));
+extern void mci_dump __P((MCI *, bool));
+extern void mci_dump_all __P((bool));
+extern MCI **mci_scan __P((MCI *));
+extern int mci_traverse_persistent __P((int (*)(), char *));
+extern int mci_print_persistent __P((char *, char *));
+extern int mci_purge_persistent __P((char *, char *));
+extern int mci_lock_host __P((MCI *));
+extern void mci_unlock_host __P((MCI *));
+extern int mci_lock_host_statfile __P((MCI *));
+extern void mci_store_persistent __P((MCI *));
+extern int mci_read_persistent __P((FILE *, MCI *));
+ /*
+** Header structure.
+** This structure is used internally to store header items.
+*/
+
+struct header
+{
+ char *h_field; /* the name of the field */
+ char *h_value; /* the value of that field */
+ struct header *h_link; /* the next header */
+ u_short h_flags; /* status bits, see below */
+ BITMAP h_mflags; /* m_flags bits needed */
+};
+
+typedef struct header HDR;
+
+/*
+** Header information structure.
+** Defined in conf.c, this struct declares the header fields
+** that have some magic meaning.
+*/
+
+struct hdrinfo
+{
+ char *hi_field; /* the name of the field */
+ u_short hi_flags; /* status bits, see below */
+ char *hi_ruleset; /* validity check ruleset */
+};
+
+extern struct hdrinfo HdrInfo[];
+
+/* bits for h_flags and hi_flags */
+# define H_EOH 0x0001 /* this field terminates header */
+# define H_RCPT 0x0002 /* contains recipient addresses */
+# define H_DEFAULT 0x0004 /* if another value is found, drop this */
+# define H_RESENT 0x0008 /* this address is a "Resent-..." address */
+# define H_CHECK 0x0010 /* check h_mflags against m_flags */
+# define H_ACHECK 0x0020 /* ditto, but always (not just default) */
+# define H_FORCE 0x0040 /* force this field, even if default */
+# define H_TRACE 0x0080 /* this field contains trace information */
+# define H_FROM 0x0100 /* this is a from-type field */
+# define H_VALID 0x0200 /* this field has a validated value */
+# define H_RECEIPTTO 0x0400 /* this field has return receipt info */
+# define H_ERRORSTO 0x0800 /* this field has error address info */
+# define H_CTE 0x1000 /* this field is a content-transfer-encoding */
+# define H_CTYPE 0x2000 /* this is a content-type field */
+# define H_BCC 0x4000 /* Bcc: header: strip value or delete */
+# define H_ENCODABLE 0x8000 /* field can be RFC 1522 encoded */
+
+/* functions */
+extern void addheader __P((char *, char *, HDR **));
+extern char *hvalue __P((char *, HDR *));
+extern void commaize __P((HDR *, char *, bool, MCI *, ENVELOPE *));
+extern void put_vanilla_header __P((HDR *, char *, MCI *));
+extern void eatheader __P((ENVELOPE *, bool));
+extern int chompheader __P((char *, bool, HDR **, ENVELOPE *));
+ /*
+** Envelope structure.
+** This structure defines the message itself. There is usually
+** only one of these -- for the message that we originally read
+** and which is our primary interest -- but other envelopes can
+** be generated during processing. For example, error messages
+** will have their own envelope.
+*/
+
+struct envelope
+{
+ HDR *e_header; /* head of header list */
+ long e_msgpriority; /* adjusted priority of this message */
+ time_t e_ctime; /* time message appeared in the queue */
+ char *e_to; /* the target person */
+ ADDRESS e_from; /* the person it is from */
+ char *e_sender; /* e_from.q_paddr w comments stripped */
+ char **e_fromdomain; /* the domain part of the sender */
+ ADDRESS *e_sendqueue; /* list of message recipients */
+ ADDRESS *e_errorqueue; /* the queue for error responses */
+ long e_msgsize; /* size of the message in bytes */
+ long e_flags; /* flags, see below */
+ int e_nrcpts; /* number of recipients */
+ short e_class; /* msg class (priority, junk, etc.) */
+ short e_hopcount; /* number of times processed */
+ short e_nsent; /* number of sends since checkpoint */
+ short e_sendmode; /* message send mode */
+ short e_errormode; /* error return mode */
+ short e_timeoutclass; /* message timeout class */
+ void (*e_puthdr)__P((MCI *, HDR *, ENVELOPE *));
+ /* function to put header of message */
+ void (*e_putbody)__P((MCI *, ENVELOPE *, char *));
+ /* function to put body of message */
+ struct envelope *e_parent; /* the message this one encloses */
+ struct envelope *e_sibling; /* the next envelope of interest */
+ char *e_bodytype; /* type of message body */
+ FILE *e_dfp; /* temporary file */
+ char *e_id; /* code for this entry in queue */
+ FILE *e_xfp; /* transcript file */
+ FILE *e_lockfp; /* the lock file for this message */
+ char *e_message; /* error message */
+ char *e_statmsg; /* stat msg (changes per delivery) */
+ char *e_msgboundary; /* MIME-style message part boundary */
+ char *e_origrcpt; /* original recipient (one only) */
+ char *e_envid; /* envelope id from MAIL FROM: line */
+ char *e_status; /* DSN status for this message */
+ time_t e_dtime; /* time of last delivery attempt */
+ int e_ntries; /* number of delivery attempts */
+ dev_t e_dfdev; /* df file's device, for crash recov */
+ ino_t e_dfino; /* df file's ino, for crash recovery */
+ char *e_macro[256]; /* macro definitions */
+};
+
+/* values for e_flags */
+#define EF_OLDSTYLE 0x0000001 /* use spaces (not commas) in hdrs */
+#define EF_INQUEUE 0x0000002 /* this message is fully queued */
+#define EF_NO_BODY_RETN 0x0000004 /* omit message body on error */
+#define EF_CLRQUEUE 0x0000008 /* disk copy is no longer needed */
+#define EF_SENDRECEIPT 0x0000010 /* send a return receipt */
+#define EF_FATALERRS 0x0000020 /* fatal errors occured */
+#define EF_DELETE_BCC 0x0000040 /* delete Bcc: headers entirely */
+#define EF_RESPONSE 0x0000080 /* this is an error or return receipt */
+#define EF_RESENT 0x0000100 /* this message is being forwarded */
+#define EF_VRFYONLY 0x0000200 /* verify only (don't expand aliases) */
+#define EF_WARNING 0x0000400 /* warning message has been sent */
+#define EF_QUEUERUN 0x0000800 /* this envelope is from queue */
+#define EF_GLOBALERRS 0x0001000 /* treat errors as global */
+#define EF_PM_NOTIFY 0x0002000 /* send return mail to postmaster */
+#define EF_METOO 0x0004000 /* send to me too */
+#define EF_LOGSENDER 0x0008000 /* need to log the sender */
+#define EF_NORECEIPT 0x0010000 /* suppress all return-receipts */
+#define EF_HAS8BIT 0x0020000 /* at least one 8-bit char in body */
+#define EF_NL_NOT_EOL 0x0040000 /* don't accept raw NL as EOLine */
+#define EF_CRLF_NOT_EOL 0x0080000 /* don't accept CR-LF as EOLine */
+#define EF_RET_PARAM 0x0100000 /* RCPT command had RET argument */
+#define EF_HAS_DF 0x0200000 /* set when df file is instantiated */
+#define EF_IS_MIME 0x0400000 /* really is a MIME message */
+#define EF_DONT_MIME 0x0800000 /* never MIME this message */
+#define EF_DISCARD 0x1000000 /* discard the message */
+
+EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */
+
+/* functions */
+extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *));
+extern void dropenvelope __P((ENVELOPE *, bool));
+extern void clearenvelope __P((ENVELOPE *, bool));
+
+extern void putheader __P((MCI *, HDR *, ENVELOPE *));
+extern void putbody __P((MCI *, ENVELOPE *, char *));
+ /*
+** Message priority classes.
+**
+** The message class is read directly from the Priority: header
+** field in the message.
+**
+** CurEnv->e_msgpriority is the number of bytes in the message plus
+** the creation time (so that jobs ``tend'' to be ordered correctly),
+** adjusted by the message class, the number of recipients, and the
+** amount of time the message has been sitting around. This number
+** is used to order the queue. Higher values mean LOWER priority.
+**
+** Each priority class point is worth WkClassFact priority points;
+** each recipient is worth WkRecipFact priority points. Each time
+** we reprocess a message the priority is adjusted by WkTimeFact.
+** WkTimeFact should normally decrease the priority so that jobs
+** that have historically failed will be run later; thanks go to
+** Jay Lepreau at Utah for pointing out the error in my thinking.
+**
+** The "class" is this number, unadjusted by the age or size of
+** this message. Classes with negative representations will have
+** error messages thrown away if they are not local.
+*/
+
+struct priority
+{
+ char *pri_name; /* external name of priority */
+ int pri_val; /* internal value for same */
+};
+
+EXTERN struct priority Priorities[MAXPRIORITIES];
+EXTERN int NumPriorities; /* pointer into Priorities */
+ /*
+** Rewrite rules.
+*/
+
+struct rewrite
+{
+ char **r_lhs; /* pattern match */
+ char **r_rhs; /* substitution value */
+ struct rewrite *r_next;/* next in chain */
+};
+
+EXTERN struct rewrite *RewriteRules[MAXRWSETS];
+
+/*
+** Special characters in rewriting rules.
+** These are used internally only.
+** The COND* rules are actually used in macros rather than in
+** rewriting rules, but are given here because they
+** cannot conflict.
+*/
+
+/* left hand side items */
+# define MATCHZANY ((u_char)0220) /* match zero or more tokens */
+# define MATCHANY ((u_char)0221) /* match one or more tokens */
+# define MATCHONE ((u_char)0222) /* match exactly one token */
+# define MATCHCLASS ((u_char)0223) /* match one token in a class */
+# define MATCHNCLASS ((u_char)0224) /* match anything not in class */
+# define MATCHREPL ((u_char)0225) /* replacement on RHS for above */
+
+/* right hand side items */
+# define CANONNET ((u_char)0226) /* canonical net, next token */
+# define CANONHOST ((u_char)0227) /* canonical host, next token */
+# define CANONUSER ((u_char)0230) /* canonical user, next N tokens */
+# define CALLSUBR ((u_char)0231) /* call another rewriting set */
+
+/* conditionals in macros */
+# define CONDIF ((u_char)0232) /* conditional if-then */
+# define CONDELSE ((u_char)0233) /* conditional else */
+# define CONDFI ((u_char)0234) /* conditional fi */
+
+/* bracket characters for host name lookup */
+# define HOSTBEGIN ((u_char)0235) /* hostname lookup begin */
+# define HOSTEND ((u_char)0236) /* hostname lookup end */
+
+/* bracket characters for generalized lookup */
+# define LOOKUPBEGIN ((u_char)0205) /* generalized lookup begin */
+# define LOOKUPEND ((u_char)0206) /* generalized lookup end */
+
+/* macro substitution character */
+# define MACROEXPAND ((u_char)0201) /* macro expansion */
+# define MACRODEXPAND ((u_char)0202) /* deferred macro expansion */
+
+/* to make the code clearer */
+# define MATCHZERO CANONHOST
+
+/* external <==> internal mapping table */
+struct metamac
+{
+ char metaname; /* external code (after $) */
+ u_char metaval; /* internal code (as above) */
+};
+
+/* values for macros with external names only */
+# define MID_OPMODE 0202 /* operation mode */
+
+/* functions */
+extern void expand __P((char *, char *, size_t, ENVELOPE *));
+extern void define __P((int, char *, ENVELOPE *));
+extern char *macvalue __P((int, ENVELOPE *));
+extern char *macname __P((int));
+extern int macid __P((char *, char **));
+ /*
+** Name canonification short circuit.
+**
+** If the name server for a host is down, the process of trying to
+** canonify the name can hang. This is similar to (but alas, not
+** identical to) looking up the name for delivery. This stab type
+** caches the result of the name server lookup so we don't hang
+** multiple times.
+*/
+
+#define NAMECANON struct _namecanon
+
+NAMECANON
+{
+ short nc_errno; /* cached errno */
+ short nc_herrno; /* cached h_errno */
+ short nc_stat; /* cached exit status code */
+ short nc_flags; /* flag bits */
+ char *nc_cname; /* the canonical name */
+};
+
+/* values for nc_flags */
+#define NCF_VALID 0x0001 /* entry valid */
+ /*
+** Mapping functions
+**
+** These allow arbitrary mappings in the config file. The idea
+** (albeit not the implementation) comes from IDA sendmail.
+*/
+
+# define MAPCLASS struct _mapclass
+# define MAP struct _map
+# define MAXMAPACTIONS 3 /* size of map_actions array */
+
+
+/*
+** An actual map.
+*/
+
+MAP
+{
+ MAPCLASS *map_class; /* the class of this map */
+ char *map_mname; /* name of this map */
+ long map_mflags; /* flags, see below */
+ char *map_file; /* the (nominal) filename */
+ ARBPTR_T map_db1; /* the open database ptr */
+ ARBPTR_T map_db2; /* an "extra" database pointer */
+ char *map_keycolnm; /* key column name */
+ char *map_valcolnm; /* value column name */
+ u_char map_keycolno; /* key column number */
+ u_char map_valcolno; /* value column number */
+ char map_coldelim; /* column delimiter */
+ char *map_app; /* to append to successful matches */
+ char *map_tapp; /* to append to "tempfail" matches */
+ char *map_domain; /* the (nominal) NIS domain */
+ char *map_rebuild; /* program to run to do auto-rebuild */
+ time_t map_mtime; /* last database modification time */
+ int map_lockfd; /* auxiliary lock file descriptor */
+ short map_specificity; /* specificity of aliases */
+ MAP *map_stack[MAXMAPSTACK]; /* list for stacked maps */
+ short map_return[MAXMAPACTIONS]; /* return bitmaps for stacked maps */
+};
+
+/* bit values for map_mflags */
+# define MF_VALID 0x00000001 /* this entry is valid */
+# define MF_INCLNULL 0x00000002 /* include null byte in key */
+# define MF_OPTIONAL 0x00000004 /* don't complain if map not found */
+# define MF_NOFOLDCASE 0x00000008 /* don't fold case in keys */
+# define MF_MATCHONLY 0x00000010 /* don't use the map value */
+# define MF_OPEN 0x00000020 /* this entry is open */
+# define MF_WRITABLE 0x00000040 /* open for writing */
+# define MF_ALIAS 0x00000080 /* this is an alias file */
+# define MF_TRY0NULL 0x00000100 /* try with no null byte */
+# define MF_TRY1NULL 0x00000200 /* try with the null byte */
+# define MF_LOCKED 0x00000400 /* this map is currently locked */
+# define MF_ALIASWAIT 0x00000800 /* alias map in aliaswait state */
+# define MF_IMPL_HASH 0x00001000 /* implicit: underlying hash database */
+# define MF_IMPL_NDBM 0x00002000 /* implicit: underlying NDBM database */
+# define MF_UNSAFEDB 0x00004000 /* this map is world writable */
+# define MF_APPEND 0x00008000 /* append new entry on rebuiled */
+# define MF_KEEPQUOTES 0x00010000 /* don't dequote key before lookup */
+# define MF_NODEFER 0x00020000 /* don't defer if map lookup fails */
+# define MF_REGEX_NOT 0x00040000 /* regular expression negation */
+
+/* indices for map_actions */
+# define MA_NOTFOUND 0 /* member map returned "not found" */
+# define MA_UNAVAIL 1 /* member map is not available */
+# define MA_TRYAGAIN 2 /* member map returns temp failure */
+
+/*
+** The class of a map -- essentially the functions to call
+*/
+
+MAPCLASS
+{
+ char *map_cname; /* name of this map class */
+ char *map_ext; /* extension for database file */
+ short map_cflags; /* flag bits, see below */
+ bool (*map_parse)__P((MAP *, char *));
+ /* argument parsing function */
+ char *(*map_lookup)__P((MAP *, char *, char **, int *));
+ /* lookup function */
+ void (*map_store)__P((MAP *, char *, char *));
+ /* store function */
+ bool (*map_open)__P((MAP *, int));
+ /* open function */
+ void (*map_close)__P((MAP *));
+ /* close function */
+};
+
+/* bit values for map_cflags */
+#define MCF_ALIASOK 0x0001 /* can be used for aliases */
+#define MCF_ALIASONLY 0x0002 /* usable only for aliases */
+#define MCF_REBUILDABLE 0x0004 /* can rebuild alias files */
+#define MCF_OPTFILE 0x0008 /* file name is optional */
+
+/* functions */
+extern char *map_rewrite __P((MAP *, const char *, size_t, char **));
+extern MAP *makemapentry __P((char *));
+extern void initmaps __P((bool, ENVELOPE *));
+ /*
+** Symbol table definitions
+*/
+
+struct symtab
+{
+ char *s_name; /* name to be entered */
+ short s_type; /* general type (see below) */
+ short s_len; /* length of this entry */
+ struct symtab *s_next; /* pointer to next in chain */
+ union
+ {
+ BITMAP sv_class; /* bit-map of word classes */
+ ADDRESS *sv_addr; /* pointer to address header */
+ MAILER *sv_mailer; /* pointer to mailer */
+ char *sv_alias; /* alias */
+ MAPCLASS sv_mapclass; /* mapping function class */
+ MAP sv_map; /* mapping function */
+ char *sv_hostsig; /* host signature */
+ MCI sv_mci; /* mailer connection info */
+ NAMECANON sv_namecanon; /* canonical name cache */
+ int sv_macro; /* macro name => id mapping */
+ int sv_ruleset; /* ruleset index */
+ struct hdrinfo sv_header; /* header metainfo */
+ char *sv_service[MAXMAPSTACK]; /* service switch */
+ } s_value;
+};
+
+typedef struct symtab STAB;
+
+/* symbol types */
+# define ST_UNDEF 0 /* undefined type */
+# define ST_CLASS 1 /* class map */
+# define ST_ADDRESS 2 /* an address in parsed format */
+# define ST_MAILER 3 /* a mailer header */
+# define ST_ALIAS 4 /* an alias */
+# define ST_MAPCLASS 5 /* mapping function class */
+# define ST_MAP 6 /* mapping function */
+# define ST_HOSTSIG 7 /* host signature */
+# define ST_NAMECANON 8 /* cached canonical name */
+# define ST_MACRO 9 /* macro name to id mapping */
+# define ST_RULESET 10 /* ruleset index */
+# define ST_SERVICE 11 /* service switch entry */
+# define ST_HEADER 12 /* special header flags */
+# define ST_MCI 16 /* mailer connection info (offset) */
+
+# define s_class s_value.sv_class
+# define s_address s_value.sv_addr
+# define s_mailer s_value.sv_mailer
+# define s_alias s_value.sv_alias
+# define s_mci s_value.sv_mci
+# define s_mapclass s_value.sv_mapclass
+# define s_hostsig s_value.sv_hostsig
+# define s_map s_value.sv_map
+# define s_namecanon s_value.sv_namecanon
+# define s_macro s_value.sv_macro
+# define s_ruleset s_value.sv_ruleset
+# define s_service s_value.sv_service
+# define s_header s_value.sv_header
+
+extern STAB *stab __P((char *, int, int));
+extern void stabapply __P((void (*)(STAB *, int), int));
+
+/* opcodes to stab */
+# define ST_FIND 0 /* find entry */
+# define ST_ENTER 1 /* enter if not there */
+ /*
+** STRUCT EVENT -- event queue.
+**
+** Maintained in sorted order.
+**
+** We store the pid of the process that set this event to insure
+** that when we fork we will not take events intended for the parent.
+*/
+
+struct event
+{
+ time_t ev_time; /* time of the function call */
+ void (*ev_func)__P((int));
+ /* function to call */
+ int ev_arg; /* argument to ev_func */
+ int ev_pid; /* pid that set this event */
+ struct event *ev_link; /* link to next item */
+};
+
+typedef struct event EVENT;
+
+EXTERN EVENT *EventQueue; /* head of event queue */
+
+/* functions */
+extern EVENT *setevent __P((time_t, void(*)(), int));
+extern void clrevent __P((EVENT *));
+ /*
+** Operation, send, error, and MIME modes
+**
+** The operation mode describes the basic operation of sendmail.
+** This can be set from the command line, and is "send mail" by
+** default.
+**
+** The send mode tells how to send mail. It can be set in the
+** configuration file. It's setting determines how quickly the
+** mail will be delivered versus the load on your system. If the
+** -v (verbose) flag is given, it will be forced to SM_DELIVER
+** mode.
+**
+** The error mode tells how to return errors.
+*/
+
+EXTERN char OpMode; /* operation mode, see below */
+
+#define MD_DELIVER 'm' /* be a mail sender */
+#define MD_SMTP 's' /* run SMTP on standard input */
+#define MD_ARPAFTP 'a' /* obsolete ARPANET mode (Grey Book) */
+#define MD_DAEMON 'd' /* run as a daemon */
+#define MD_FGDAEMON 'D' /* run daemon in foreground */
+#define MD_VERIFY 'v' /* verify: don't collect or deliver */
+#define MD_TEST 't' /* test mode: resolve addrs only */
+#define MD_INITALIAS 'i' /* initialize alias database */
+#define MD_PRINT 'p' /* print the queue */
+#define MD_FREEZE 'z' /* freeze the configuration file */
+#define MD_HOSTSTAT 'h' /* print persistent host stat info */
+#define MD_PURGESTAT 'H' /* purge persistent host stat info */
+
+/* values for e_sendmode -- send modes */
+#define SM_DELIVER 'i' /* interactive delivery */
+#define SM_FORK 'b' /* deliver in background */
+#define SM_QUEUE 'q' /* queue, don't deliver */
+#define SM_DEFER 'd' /* defer map lookups as well as queue */
+#define SM_VERIFY 'v' /* verify only (used internally) */
+
+/* used only as a parameter to sendall */
+#define SM_DEFAULT '\0' /* unspecified, use SendMode */
+
+
+/* values for e_errormode -- error handling modes */
+#define EM_PRINT 'p' /* print errors */
+#define EM_MAIL 'm' /* mail back errors */
+#define EM_WRITE 'w' /* write back errors */
+#define EM_BERKNET 'e' /* special berknet processing */
+#define EM_QUIET 'q' /* don't print messages (stat only) */
+
+
+/* MIME processing mode */
+EXTERN int MimeMode;
+
+/* bit values for MimeMode */
+#define MM_CVTMIME 0x0001 /* convert 8 to 7 bit MIME */
+#define MM_PASS8BIT 0x0002 /* just send 8 bit data blind */
+#define MM_MIME8BIT 0x0004 /* convert 8-bit data to MIME */
+
+/* queue sorting order algorithm */
+EXTERN int QueueSortOrder;
+
+#define QS_BYPRIORITY 0 /* sort by message priority */
+#define QS_BYHOST 1 /* sort by first host name */
+#define QS_BYTIME 2 /* sort by submission time */
+
+
+/* how to handle messages without any recipient addresses */
+EXTERN int NoRecipientAction;
+
+#define NRA_NO_ACTION 0 /* just leave it as is */
+#define NRA_ADD_TO 1 /* add To: header */
+#define NRA_ADD_APPARENTLY_TO 2 /* add Apparently-To: header */
+#define NRA_ADD_BCC 3 /* add empty Bcc: header */
+#define NRA_ADD_TO_UNDISCLOSED 4 /* add To: undisclosed:; header */
+
+
+/* flags to putxline */
+#define PXLF_NOTHINGSPECIAL 0 /* no special mapping */
+#define PXLF_MAPFROM 0x0001 /* map From_ to >From_ */
+#define PXLF_STRIP8BIT 0x0002 /* strip 8th bit */
+#define PXLF_HEADER 0x0004 /* map newlines in headers */
+ /*
+** Additional definitions
+*/
+
+
+/*
+** Privacy flags
+** These are bit values for the PrivacyFlags word.
+*/
+
+#define PRIV_PUBLIC 0 /* what have I got to hide? */
+#define PRIV_NEEDMAILHELO 0x0001 /* insist on HELO for MAIL, at least */
+#define PRIV_NEEDEXPNHELO 0x0002 /* insist on HELO for EXPN */
+#define PRIV_NEEDVRFYHELO 0x0004 /* insist on HELO for VRFY */
+#define PRIV_NOEXPN 0x0008 /* disallow EXPN command entirely */
+#define PRIV_NOVRFY 0x0010 /* disallow VRFY command entirely */
+#define PRIV_AUTHWARNINGS 0x0020 /* flag possible authorization probs */
+#define PRIV_NORECEIPTS 0x0040 /* disallow return receipts */
+#define PRIV_NOETRN 0x0080 /* disallow ETRN command entirely */
+#define PRIV_NOVERB 0x0100 /* disallow VERB command entirely */
+#define PRIV_RESTRICTMAILQ 0x1000 /* restrict mailq command */
+#define PRIV_RESTRICTQRUN 0x2000 /* restrict queue run */
+#define PRIV_GOAWAY 0x0fff /* don't give no info, anyway, anyhow */
+
+/* struct defining such things */
+struct prival
+{
+ char *pv_name; /* name of privacy flag */
+ int pv_flag; /* numeric level */
+};
+
+
+/*
+** Flags passed to remotename, parseaddr, allocaddr, and buildaddr.
+*/
+
+#define RF_SENDERADDR 0x001 /* this is a sender address */
+#define RF_HEADERADDR 0x002 /* this is a header address */
+#define RF_CANONICAL 0x004 /* strip comment information */
+#define RF_ADDDOMAIN 0x008 /* OK to do domain extension */
+#define RF_COPYPARSE 0x010 /* copy parsed user & host */
+#define RF_COPYPADDR 0x020 /* copy print address */
+#define RF_COPYALL (RF_COPYPARSE|RF_COPYPADDR)
+#define RF_COPYNONE 0
+
+
+/*
+** Flags passed to safefile/safedirpath.
+*/
+
+#define SFF_ANYFILE 0 /* no special restrictions */
+#define SFF_MUSTOWN 0x0001 /* user must own this file */
+#define SFF_NOSLINK 0x0002 /* file cannot be a symbolic link */
+#define SFF_ROOTOK 0x0004 /* ok for root to own this file */
+#define SFF_RUNASREALUID 0x0008 /* if no ctladdr, run as real uid */
+#define SFF_NOPATHCHECK 0x0010 /* don't bother checking dir path */
+#define SFF_SETUIDOK 0x0020 /* setuid files are ok */
+#define SFF_CREAT 0x0040 /* ok to create file if necessary */
+#define SFF_REGONLY 0x0080 /* regular files only */
+#define SFF_SAFEDIRPATH 0x0100 /* no writable directories allowed */
+#define SFF_NOHLINK 0x0200 /* file cannot have hard links */
+#define SFF_NOWLINK 0x0400 /* links only in non-writable dirs */
+#define SFF_NOGWFILES 0x0800 /* disallow world writable files */
+#define SFF_NOWWFILES 0x1000 /* disallow group writable files */
+
+/* flags that are actually specific to safeopen/safefopen/dfopen */
+#define SFF_OPENASROOT 0x2000 /* open as root instead of real user */
+#define SFF_NOLOCK 0x4000 /* don't lock the file */
+
+/* pseudo-flags */
+#define SFF_NOLINK (SFF_NOHLINK|SFF_NOSLINK)
+
+/* functions */
+extern int safefile __P((char *, UID_T, GID_T, char *, int, int, struct stat *));
+extern int safedirpath __P((char *, UID_T, GID_T, char *, int));
+extern int safeopen __P((char *, int, int, int));
+extern FILE *safefopen __P((char *, int, int, int));
+extern int dfopen __P((char *, int, int, int));
+extern bool filechanged __P((char *, int, struct stat *));
+
+
+/*
+** Flags passed to mime8to7.
+*/
+
+#define M87F_OUTER 0 /* outer context */
+#define M87F_NO8BIT 0x0001 /* can't have 8-bit in this section */
+#define M87F_DIGEST 0x0002 /* processing multipart/digest */
+
+
+/*
+** Flags passed to returntosender.
+*/
+
+#define RTSF_NO_BODY 0 /* send headers only */
+#define RTSF_SEND_BODY 0x0001 /* include body of message in return */
+#define RTSF_PM_BOUNCE 0x0002 /* this is a postmaster bounce */
+
+
+/*
+** Regular UNIX sockaddrs are too small to handle ISO addresses, so
+** we are forced to declare a supertype here.
+*/
+
+# if NETINET || NETUNIX || NETISO || NETNS || NETX25
+union bigsockaddr
+{
+ struct sockaddr sa; /* general version */
+#if NETUNIX
+ struct sockaddr_un sunix; /* UNIX family */
+#endif
+#if NETINET
+ struct sockaddr_in sin; /* INET family */
+#endif
+#if NETISO
+ struct sockaddr_iso siso; /* ISO family */
+#endif
+#if NETNS
+ struct sockaddr_ns sns; /* XNS family */
+#endif
+#if NETX25
+ struct sockaddr_x25 sx25; /* X.25 family */
+#endif
+};
+
+#define SOCKADDR union bigsockaddr
+
+EXTERN SOCKADDR RealHostAddr; /* address of host we are talking to */
+
+extern char *hostnamebyanyaddr __P((SOCKADDR *));
+extern char *anynet_ntoa __P((SOCKADDR *));
+# if DAEMON
+extern char *validate_connection __P((SOCKADDR *, char *, ENVELOPE *));
+# endif
+
+#endif
+
+
+/*
+** Vendor codes
+**
+** Vendors can customize sendmail to add special behaviour,
+** generally for back compatibility. Ideally, this should
+** be set up in the .cf file using the "V" command. However,
+** it's quite reasonable for some vendors to want the default
+** be their old version; this can be set using
+** -DVENDOR_DEFAULT=VENDOR_xxx
+** in the Makefile.
+**
+** Vendors should apply to sendmail@CS.Berkeley.EDU for
+** unique vendor codes.
+*/
+
+#define VENDOR_BERKELEY 1 /* Berkeley-native configuration file */
+#define VENDOR_SUN 2 /* Sun-native configuration file */
+#define VENDOR_HP 3 /* Hewlett-Packard specific config syntax */
+#define VENDOR_IBM 4 /* IBM specific config syntax */
+
+EXTERN int VendorCode; /* vendor-specific operation enhancements */
+
+/* prototypes for vendor-specific hook routines */
+extern void vendor_set_uid __P((UID_T));
+extern void vendor_daemon_setup __P((ENVELOPE *));
+
+/*
+** DontBlameSendmail options
+**
+** Hopefully nobody uses these.
+*/
+#define DBS_SAFE 0
+#define DBS_ASSUMESAFECHOWN 0x00000001
+#define DBS_GROUPWRITABLEDIRPATHSAFE 0x00000002
+#define DBS_GROUPWRITABLEFORWARDFILESAFE 0x00000004
+#define DBS_GROUPWRITABLEINCLUDEFILESAFE 0x00000008
+#define DBS_GROUPWRITABLEALIASFILE 0x00000010
+#define DBS_WORLDWRITABLEALIASFILE 0x00000020
+#define DBS_FORWARDFILEINUNSAFEDIRPATH 0x00000040
+#define DBS_INCLUDEFILEINUNSAFEDIRPATH 0x00000060
+#define DBS_MAPINUNSAFEDIRPATH 0x00000080
+#define DBS_LINKEDALIASFILEINWRITABLEDIR 0x00000100
+#define DBS_LINKEDCLASSFILEINWRITABLEDIR 0x00000200
+#define DBS_LINKEDFORWARDFILEINWRITABLEDIR 0x00000400
+#define DBS_LINKEDINCLUDEFILEINWRITABLEDIR 0x00000800
+#define DBS_LINKEDMAPINWRITABLEDIR 0x00001000
+#define DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR 0x00002000
+#define DBS_FILEDELIVERYTOHARDLINK 0x00004000
+#define DBS_FILEDELIVERYTOSYMLINK 0x00008000
+#define DBS_WRITEMAPTOHARDLINK 0x00010000
+#define DBS_WRITEMAPTOSYMLINK 0x00020000
+#define DBS_WRITESTATSTOHARDLINK 0x00040000
+#define DBS_WRITESTATSTOSYMLINK 0x00080000
+#define DBS_FORWARDFILEINGROUPWRITABLEDIRPATH 0x00100000
+#define DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH 0x00200000
+#define DBS_CLASSFILEINUNSAFEDIRPATH 0x00400000
+#define DBS_ERRORHEADERINUNSAFEDIRPATH 0x00800000
+#define DBS_HELPFILEINUNSAFEDIRPATH 0x01000000
+#define DBS_FORWARDFILEINUNSAFEDIRPATHSAFE 0x02000000
+#define DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE 0x04000000
+#define DBS_RUNPROGRAMINUNSAFEDIRPATH 0x08000000
+#define DBS_RUNWRITABLEPROGRAM 0x10000000
+
+/* struct defining such things */
+struct dbsval
+{
+ char *dbs_name; /* name of DontBlameSendmail flag */
+ long dbs_flag; /* numeric level */
+};
+
+EXTERN long DontBlameSendmail; /* DontBlameSendmail option bits */
+
+/*
+** Terminal escape codes.
+**
+** To make debugging output clearer.
+*/
+
+struct termescape
+{
+ char *te_rv_on; /* turn reverse-video on */
+ char *te_rv_off; /* turn reverse-video off */
+};
+
+EXTERN struct termescape TermEscape;
+
+
+/*
+** Error return from inet_addr(3), in case not defined in /usr/include.
+*/
+
+#ifndef INADDR_NONE
+# define INADDR_NONE 0xffffffff
+#endif
+ /*
+** Global variables.
+*/
+
+EXTERN bool FromFlag; /* if set, "From" person is explicit */
+EXTERN bool MeToo; /* send to the sender also */
+EXTERN bool IgnrDot; /* don't let dot end messages */
+EXTERN bool SaveFrom; /* save leading "From" lines */
+EXTERN bool GrabTo; /* if set, get recipients from msg */
+EXTERN bool SuprErrs; /* set if we are suppressing errors */
+EXTERN bool HoldErrs; /* only output errors to transcript */
+EXTERN bool NoConnect; /* don't connect to non-local mailers */
+EXTERN bool SuperSafe; /* be extra careful, even if expensive */
+EXTERN bool ForkQueueRuns; /* fork for each job when running the queue */
+EXTERN bool AutoRebuild; /* auto-rebuild the alias database as needed */
+EXTERN bool CheckAliases; /* parse addresses during newaliases */
+EXTERN bool NoAlias; /* suppress aliasing */
+EXTERN bool UseNameServer; /* using DNS -- interpret h_errno & MX RRs */
+EXTERN bool UseHesiod; /* using Hesiod -- interpret Hesiod errors */
+EXTERN bool SevenBitInput; /* force 7-bit data on input */
+EXTERN bool HasEightBits; /* has at least one eight bit input byte */
+EXTERN bool ConfigFileRead; /* configuration file has been read */
+EXTERN time_t SafeAlias; /* interval to wait until @:@ in alias file */
+EXTERN FILE *InChannel; /* input connection */
+EXTERN FILE *OutChannel; /* output connection */
+EXTERN char *RealUserName; /* real user name of caller */
+EXTERN uid_t RealUid; /* real uid of caller */
+EXTERN gid_t RealGid; /* real gid of caller */
+EXTERN uid_t DefUid; /* default uid to run as */
+EXTERN gid_t DefGid; /* default gid to run as */
+EXTERN char *DefUser; /* default user to run as (from DefUid) */
+EXTERN uid_t TrustedFileUid; /* uid of trusted owner of files and dirs */
+EXTERN MODE_T OldUmask; /* umask when sendmail starts up */
+EXTERN int Verbose; /* set if blow-by-blow desired */
+EXTERN int Errors; /* set if errors (local to single pass) */
+EXTERN int ExitStat; /* exit status code */
+EXTERN int LineNumber; /* line number in current input */
+EXTERN int LogLevel; /* level of logging to perform */
+EXTERN int FileMode; /* mode on files */
+EXTERN int QueueLA; /* load average starting forced queueing */
+EXTERN int RefuseLA; /* load average refusing connections are */
+EXTERN int CurrentLA; /* current load average */
+EXTERN long QueueFactor; /* slope of queue function */
+EXTERN time_t QueueIntvl; /* intervals between running the queue */
+EXTERN char *HelpFile; /* location of SMTP help file */
+EXTERN char *ErrMsgFile; /* file to prepend to all error messages */
+EXTERN char *StatFile; /* location of statistics summary */
+EXTERN char *QueueDir; /* location of queue directory */
+EXTERN char *FileName; /* name to print on error messages */
+EXTERN char *SmtpPhase; /* current phase in SMTP processing */
+EXTERN char *MyHostName; /* name of this host for SMTP messages */
+EXTERN char *RealHostName; /* name of host we are talking to */
+EXTERN char *CurHostName; /* current host we are dealing with */
+EXTERN jmp_buf TopFrame; /* branch-to-top-of-loop-on-error frame */
+EXTERN bool QuickAbort; /* .... but only if we want a quick abort */
+EXTERN bool OnlyOneError; /* .... or only want to give one SMTP reply */
+EXTERN bool LogUsrErrs; /* syslog user errors (e.g., SMTP RCPT cmd) */
+EXTERN bool SendMIMEErrors; /* send error messages in MIME format */
+EXTERN bool MatchGecos; /* look for user names in gecos field */
+EXTERN bool UseErrorsTo; /* use Errors-To: header (back compat) */
+EXTERN bool TryNullMXList; /* if we are the best MX, try host directly */
+EXTERN bool InChild; /* true if running in an SMTP subprocess */
+EXTERN bool DisConnected; /* running with OutChannel redirected to xf */
+EXTERN bool ColonOkInAddr; /* single colon legal in address */
+EXTERN bool HasWildcardMX; /* don't use MX records when canonifying */
+EXTERN char SpaceSub; /* substitution for <lwsp> */
+EXTERN int PrivacyFlags; /* privacy flags */
+EXTERN char *ConfFile; /* location of configuration file [conf.c] */
+EXTERN char *PidFile; /* location of proc id file [conf.c] */
+extern ADDRESS NullAddress; /* a null (template) address [main.c] */
+EXTERN long WkClassFact; /* multiplier for message class -> priority */
+EXTERN long WkRecipFact; /* multiplier for # of recipients -> priority */
+EXTERN long WkTimeFact; /* priority offset each time this job is run */
+EXTERN char *UdbSpec; /* user database source spec */
+EXTERN int MaxHopCount; /* max # of hops until bounce */
+EXTERN int ConfigLevel; /* config file level */
+EXTERN char *TimeZoneSpec; /* override time zone specification */
+EXTERN char *ForwardPath; /* path to search for .forward files */
+EXTERN long MinBlocksFree; /* min # of blocks free on queue fs */
+EXTERN char *FallBackMX; /* fall back MX host */
+EXTERN long MaxMessageSize; /* advertised max size we will accept */
+EXTERN time_t MinQueueAge; /* min delivery interval */
+EXTERN time_t DialDelay; /* delay between dial-on-demand tries */
+EXTERN char *SafeFileEnv; /* chroot location for file delivery */
+EXTERN char *HostsFile; /* path to /etc/hosts file */
+EXTERN char *HostStatDir; /* location of host status information */
+EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */
+EXTERN int MaxChildren; /* maximum number of daemonic children */
+EXTERN int CurChildren; /* current number of daemonic children */
+EXTERN char *SmtpGreeting; /* SMTP greeting message (old $e macro) */
+EXTERN char *UnixFromLine; /* UNIX From_ line (old $l macro) */
+EXTERN char *OperatorChars; /* operators (old $o macro) */
+EXTERN bool DontInitGroups; /* avoid initgroups() because of NIS cost */
+EXTERN int DefaultNotify; /* default DSN notification flags */
+EXTERN bool AllowBogusHELO; /* allow syntax errors on HELO command */
+EXTERN bool UserSubmission; /* initial (user) mail submission */
+EXTERN char *RunAsUserName; /* user to become for bulk of run */
+EXTERN uid_t RunAsUid; /* UID to become for bulk of run */
+EXTERN gid_t RunAsGid; /* GID to become for bulk of run */
+EXTERN int MaxRcptPerMsg; /* max recipients per SMTP message */
+EXTERN bool DoQueueRun; /* non-interrupt time queue run needed */
+EXTERN u_long ConnectOnlyTo; /* override connection address (for testing) */
+#if _FFR_DSN_RRT_OPTION
+EXTERN bool RrtImpliesDsn; /* turn Return-Receipt-To: into DSN */
+#endif
+EXTERN char *DeadLetterDrop; /* path to dead letter office */
+EXTERN bool DontProbeInterfaces; /* don't probe interfaces for names */
+EXTERN bool ChownAlwaysSafe; /* treat chown(2) as safe */
+EXTERN bool IgnoreHostStatus; /* ignore long term host status files */
+EXTERN bool SingleThreadDelivery; /* single thread hosts on delivery */
+EXTERN bool SingleLineFromHeader; /* force From: header to be one line */
+EXTERN bool DontLockReadFiles; /* don't read lock support files */
+EXTERN int ConnRateThrottle; /* throttle for SMTP connection rate */
+EXTERN int MaxAliasRecursion; /* maximum depth of alias recursion */
+EXTERN int MaxMacroRecursion; /* maximum depth of macro recursion */
+EXTERN int MaxRuleRecursion; /* maximum depth of ruleset recursion */
+EXTERN char *MustQuoteChars; /* quote these characters in phrases */
+EXTERN char *ServiceSwitchFile; /* backup service switch */
+EXTERN char *DefaultCharSet; /* default character set for MIME */
+EXTERN char *PostMasterCopy; /* address to get errs cc's */
+EXTERN int CheckpointInterval; /* queue file checkpoint interval */
+EXTERN bool DontPruneRoutes; /* don't prune source routes */
+EXTERN bool DontExpandCnames; /* do not $[...$] expand CNAMEs */
+EXTERN int MaxMciCache; /* maximum entries in MCI cache */
+EXTERN time_t ServiceCacheTime; /* time service switch was cached */
+EXTERN time_t ServiceCacheMaxAge; /* refresh interval for cache */
+EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */
+EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */
+EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */
+EXTERN char *DoubleBounceAddr; /* where to send double bounces */
+EXTERN char **ExternalEnviron; /* input environment */
+EXTERN char *UserEnviron[MAXUSERENVIRON + 1];
+ /* saved user environment */
+extern int errno;
+
+/*
+** Queue Run Limitations
+*/
+struct queue_char
+{
+ char *queue_match; /* string to match */
+ struct queue_char *queue_next;
+};
+
+typedef struct queue_char QUEUE_CHAR;
+
+EXTERN QUEUE_CHAR *QueueLimitRecipient; /* limit queue runs to this recipient */
+EXTERN QUEUE_CHAR *QueueLimitSender; /* limit queue runs to this sender */
+EXTERN QUEUE_CHAR *QueueLimitId; /* limit queue runs to this id */
+
+/*
+** Timeouts
+**
+** Indicated values are the MINIMUM per RFC 1123 section 5.3.2.
+*/
+
+EXTERN struct
+{
+ /* RFC 1123-specified timeouts [minimum value] */
+ time_t to_initial; /* initial greeting timeout [5m] */
+ time_t to_mail; /* MAIL command [5m] */
+ time_t to_rcpt; /* RCPT command [5m] */
+ time_t to_datainit; /* DATA initiation [2m] */
+ time_t to_datablock; /* DATA block [3m] */
+ time_t to_datafinal; /* DATA completion [10m] */
+ time_t to_nextcommand; /* next command [5m] */
+ /* following timeouts are not mentioned in RFC 1123 */
+ time_t to_iconnect; /* initial connection timeout (first try) */
+ time_t to_connect; /* initial connection timeout (later tries) */
+ time_t to_rset; /* RSET command */
+ time_t to_helo; /* HELO command */
+ time_t to_quit; /* QUIT command */
+ time_t to_miscshort; /* misc short commands (NOOP, VERB, etc) */
+ time_t to_ident; /* IDENT protocol requests */
+ time_t to_fileopen; /* opening :include: and .forward files */
+ /* following are per message */
+ time_t to_q_return[MAXTOCLASS]; /* queue return timeouts */
+ time_t to_q_warning[MAXTOCLASS]; /* queue warning timeouts */
+} TimeOuts;
+
+/* timeout classes for return and warning timeouts */
+# define TOC_NORMAL 0 /* normal delivery */
+# define TOC_URGENT 1 /* urgent delivery */
+# define TOC_NONURGENT 2 /* non-urgent delivery */
+
+
+/*
+** Trace information
+*/
+
+/* trace vector and macros for debugging flags */
+EXTERN u_char tTdvect[100];
+# define tTd(flag, level) (tTdvect[flag] >= level)
+# define tTdlevel(flag) (tTdvect[flag])
+ /*
+** Miscellaneous information.
+*/
+
+
+/*
+** The "no queue id" queue id for sm_syslog
+*/
+
+#define NOQID "*~*"
+
+
+/*
+** Some in-line functions
+*/
+
+/* set exit status */
+#define setstat(s) { \
+ if (ExitStat == EX_OK || ExitStat == EX_TEMPFAIL) \
+ ExitStat = s; \
+ }
+
+/* make a copy of a string */
+#define newstr(s) strcpy(xalloc(strlen(s) + 1), s)
+
+#define STRUCTCOPY(s, d) d = s
+
+
+
+/*
+** Declarations of useful functions
+*/
+
+extern char *xalloc __P((int));
+extern char *sfgets __P((char *, int, FILE *, time_t, char *));
+extern char *queuename __P((ENVELOPE *, int));
+extern time_t curtime __P((void));
+extern bool transienterror __P((int));
+extern char *fgetfolded __P((char *, int, FILE *));
+extern char *username __P((void));
+extern char *pintvl __P((time_t, bool));
+extern bool shouldqueue __P((long, time_t));
+extern bool lockfile __P((int, char *, char *, int));
+extern char *hostsignature __P((MAILER *, char *, ENVELOPE *));
+extern void openxscript __P((ENVELOPE *));
+extern void closexscript __P((ENVELOPE *));
+extern char *shortenstring __P((const char *, int));
+extern bool usershellok __P((char *, char *));
+extern char *defcharset __P((ENVELOPE *));
+extern bool wordinclass __P((char *, int));
+extern char *denlstring __P((char *, bool, bool));
+extern void makelower __P((char *));
+extern bool rebuildaliases __P((MAP *, bool));
+extern void readaliases __P((MAP *, FILE *, bool, bool));
+extern void finis __P((void));
+extern void setsender __P((char *, ENVELOPE *, char **, int, bool));
+extern void xputs __P((const char *));
+extern void logsender __P((ENVELOPE *, char *));
+extern void smtprset __P((MAILER *, MCI *, ENVELOPE *));
+extern void smtpquit __P((MAILER *, MCI *, ENVELOPE *));
+extern void setuserenv __P((const char *, const char *));
+extern char *getextenv __P((const char *));
+extern void disconnect __P((int, ENVELOPE *));
+extern void putxline __P((char *, size_t, MCI *, int));
+extern void dumpfd __P((int, bool, bool));
+extern void makemailer __P((char *));
+extern void putfromline __P((MCI *, ENVELOPE *));
+extern void setoption __P((int, char *, bool, bool, ENVELOPE *));
+extern void setclass __P((int, char *));
+extern void inittimeouts __P((char *));
+extern void logdelivery __P((MAILER *, MCI *, const char *, ADDRESS *, time_t, ENVELOPE *));
+extern void giveresponse __P((int, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *));
+extern void buildfname __P((char *, char *, char *, int));
+extern void mci_setstat __P((MCI *, int, char *, char *));
+extern char *smtptodsn __P((int));
+extern int rscheck __P((char *, char *, char *, ENVELOPE *e));
+extern void mime7to8 __P((MCI *, HDR *, ENVELOPE *));
+extern int mime8to7 __P((MCI *, HDR *, ENVELOPE *, char **, int));
+extern void xfclose __P((FILE *, char *, char *));
+extern int switch_map_find __P((char *, char *[], short []));
+extern void shorten_hostname __P((char []));
+extern int waitfor __P((pid_t));
+extern void proc_list_add __P((pid_t));
+extern void proc_list_drop __P((pid_t));
+extern void proc_list_clear __P((void));
+extern void buffer_errors __P((void));
+extern void flush_errors __P((bool));
+extern void putline __P((char *, MCI *));
+extern bool xtextok __P((char *));
+extern char *xtextify __P((char *, char *));
+extern char *xuntextify __P((char *));
+extern void cleanstrcpy __P((char *, char *, int));
+extern int getmxrr __P((char *, char **, bool, int *));
+extern int strtorwset __P((char *, char **, int));
+extern void printav __P((char **));
+extern void printopenfds __P((bool));
+extern int endmailer __P((MCI *, ENVELOPE *, char **));
+extern void fixcrlf __P((char *, bool));
+extern int dofork __P((void));
+extern void initsys __P((ENVELOPE *));
+extern void collect __P((FILE *, bool, HDR **, ENVELOPE *));
+extern void stripquotes __P((char *));
+extern int include __P((char *, bool, ADDRESS *, ADDRESS **, int, ENVELOPE *));
+extern void unlockqueue __P((ENVELOPE *));
+extern void xunlink __P((char *));
+extern bool runqueue __P((bool, bool));
+extern int getla __P((void));
+extern void sendall __P((ENVELOPE *, int));
+extern void queueup __P((ENVELOPE *, bool));
+extern void checkfds __P((char *));
+extern int returntosender __P((char *, ADDRESS *, int, ENVELOPE *));
+extern void markstats __P((ENVELOPE *, ADDRESS *, bool));
+extern void poststats __P((char *));
+extern char *arpadate __P((char *));
+extern int mailfile __P((char *volatile, MAILER *volatile, ADDRESS *, volatile int, ENVELOPE *));
+extern void loseqfile __P((ENVELOPE *, char *));
+extern int prog_open __P((char **, int *, ENVELOPE *));
+extern bool getcanonname __P((char *, int, bool));
+extern bool path_is_dir __P((char *, bool));
+extern pid_t dowork __P((char *, bool, bool, ENVELOPE *));
+extern int drop_privileges __P((bool));
+extern void fill_fd __P((int, char *));
+
+extern const char *errstring __P((int));
+extern sigfunc_t setsignal __P((int, sigfunc_t));
+extern int blocksignal __P((int));
+extern int releasesignal __P((int));
+extern struct hostent *sm_gethostbyname __P((char *));
+extern struct hostent *sm_gethostbyaddr __P((char *, int, int));
+extern struct passwd *sm_getpwnam __P((char *));
+extern struct passwd *sm_getpwuid __P((UID_T));
+extern struct passwd *finduser __P((char *, bool *));
+
+#ifdef XDEBUG
+extern void checkfdopen __P((int, char *));
+extern void checkfd012 __P((char *));
+#endif
+
+/* ellipsis is a different case though */
+extern void auth_warning __P((ENVELOPE *, const char *, ...));
+extern void syserr __P((const char *, ...));
+extern void usrerr __P((const char *, ...));
+extern void message __P((const char *, ...));
+extern void nmessage __P((const char *, ...));
+extern void setproctitle __P((const char *, ...));
+extern void sm_syslog __P((int, const char *, const char *, ...));
+
+#if !HASSNPRINTF
+extern int snprintf __P((char *, size_t, const char *, ...));
+extern int vsnprintf __P((char *, size_t, const char *, va_list));
+#endif
+extern char *quad_to_string __P((QUAD_T));
diff --git a/contrib/sendmail/src/sendmail.hf b/contrib/sendmail/src/sendmail.hf
new file mode 100644
index 0000000..12a3060
--- /dev/null
+++ b/contrib/sendmail/src/sendmail.hf
@@ -0,0 +1,119 @@
+cpyr
+cpyr Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+cpyr Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+cpyr Copyright (c) 1988, 1993
+cpyr The Regents of the University of California. All rights reserved.
+cpyr
+cpyr
+cpyr By using this file, you agree to the terms and conditions set
+cpyr forth in the LICENSE file which can be found at the top level of
+cpyr the sendmail distribution.
+cpyr
+cpyr @(#)sendmail.hf 8.16 (Berkeley) 5/19/98
+cpyr
+smtp Topics:
+smtp HELO EHLO MAIL RCPT DATA
+smtp RSET NOOP QUIT HELP VRFY
+smtp EXPN VERB ETRN DSN
+smtp For more info use "HELP <topic>".
+smtp To report bugs in the implementation send email to
+smtp sendmail-bugs@sendmail.org.
+smtp For local information send email to Postmaster at your site.
+help HELP [ <topic> ]
+help The HELP command gives help info.
+helo HELO <hostname>
+helo Introduce yourself.
+ehlo EHLO <hostname>
+ehlo Introduce yourself, and request extended SMTP mode.
+ehlo Possible replies include:
+ehlo SEND Send as mail [RFC821]
+ehlo SOML Send as mail or terminal [RFC821]
+ehlo SAML Send as mail and terminal [RFC821]
+ehlo EXPN Expand the mailing list [RFC821]
+ehlo HELP Supply helpful information [RFC821]
+ehlo TURN Turn the operation around [RFC821]
+ehlo 8BITMIME Use 8-bit data [RFC1652]
+ehlo SIZE Message size declaration [RFC1870]
+ehlo VERB Verbose [Allman]
+ehlo ONEX One message transaction only [Allman]
+ehlo CHUNKING Chunking [RFC1830]
+ehlo BINARYMIME Binary MIME [RFC1830]
+ehlo PIPELINING Command Pipelining [RFC1854]
+ehlo DSN Delivery Status Notification [RFC1891]
+ehlo ETRN Remote Message Queue Starting [RFC1985]
+ehlo XUSR Initial (user) submission [Allman]
+mail MAIL FROM: <sender> [ <parameters> ]
+mail Specifies the sender. Parameters are ESMTP extensions.
+mail See "HELP DSN" for details.
+rcpt RCPT TO: <recipient> [ <parameters> ]
+rcpt Specifies the recipient. Can be used any number of times.
+rcpt Parameters are ESMTP extensions. See "HELP DSN" for details.
+data DATA
+data Following text is collected as the message.
+data End with a single dot.
+rset RSET
+rset Resets the system.
+quit QUIT
+quit Exit sendmail (SMTP).
+verb VERB
+verb Go into verbose mode. This sends 0xy responses that are
+verb not RFC821 standard (but should be) They are recognized
+verb by humans and other sendmail implementations.
+vrfy VRFY <recipient>
+vrfy Verify an address. If you want to see what it aliases
+vrfy to, use EXPN instead.
+expn EXPN <recipient>
+expn Expand an address. If the address indicates a mailing
+expn list, return the contents of that list.
+noop NOOP
+noop Do nothing.
+send SEND FROM: <sender>
+send replaces the MAIL command, and can be used to send
+send directly to a users terminal. Not supported in this
+send implementation.
+soml SOML FROM: <sender>
+soml Send or mail. If the user is logged in, send directly,
+soml otherwise mail. Not supported in this implementation.
+saml SAML FROM: <sender>
+saml Send and mail. Send directly to the user's terminal,
+saml and also mail a letter. Not supported in this
+saml implementation.
+turn TURN
+turn Reverses the direction of the connection. Not currently
+turn implemented.
+etrn ETRN [ <hostname> | @<domain> | #<queuename> ]
+etrn Run the queue for the specified <hostname>, or
+etrn all hosts within a given <domain>, or a specially-named
+etrn <queuename> (implementation-specific).
+dsn MAIL FROM: <sender> [ RET={ FULL | HDRS} ] [ ENVID=<envid> ]
+dsn RCPT TO: <recipient> [ NOTIFY={NEVER,SUCCESS,FAILURE,DELAY} ]
+dsn [ ORCPT=<recipient> ]
+dsn SMTP Delivery Status Notifications.
+dsn Descriptions:
+dsn RET Return either the full message or only headers.
+dsn ENVID Sender's "envelope identifier" for tracking.
+dsn NOTIFY When to send a DSN. Multiple options are OK, comma-
+dsn delimited. NEVER must appear by itself.
+dsn ORCPT Original recipient.
+-bt Help for test mode:
+-bt ? :this help message.
+-bt .Dmvalue :define macro `m' to `value'.
+-bt .Ccvalue :add `value' to class `c'.
+-bt =Sruleset :dump the contents of the indicated ruleset.
+-bt =M :display the known mailers.
+-bt -ddebug-spec :equivalent to the command-line -d debug flag.
+-bt $m :print the value of macro $m.
+-bt $=c :print the contents of class $=c.
+-bt /mx host :returns the MX records for `host'.
+-bt /parse address :parse address, returning the value of crackaddr, and
+-bt the parsed address (same as -bv).
+-bt /try mailer addr :rewrite address into the form it will have when
+-bt presented to the indicated mailer.
+-bt /tryflags flags :set flags used by parsing. The flags can be `H' for
+-bt Header or `E' for Envelope, and `S' for Sender or `R'
+-bt for Recipient. These can be combined, `HR' sets
+-bt flags for header recipients.
+-bt /canon hostname :try to canonify hostname.
+-bt /map mapname key :look up `key' in the indicated `mapname'.
+-bt rules addr :run the indicated address through the named rules.
+-bt Rules can be a comma separated list of rules.
diff --git a/contrib/sendmail/src/snprintf.c b/contrib/sendmail/src/snprintf.c
new file mode 100644
index 0000000..9e0b6f1
--- /dev/null
+++ b/contrib/sendmail/src/snprintf.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)snprintf.c 8.11 (Berkeley) 6/4/98";
+#endif /* not lint */
+
+#include "sendmail.h"
+
+ /*
+** SNPRINTF, VSNPRINT -- counted versions of printf
+**
+** These versions have been grabbed off the net. They have been
+** cleaned up to compile properly and support for .precision and
+** %lx has been added.
+*/
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (sm_dopr) included.
+ * Sigh. This sort of thing is always nasty do deal with. Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length. This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ **************************************************************/
+
+/*static char _id[] = "$Id: snprintf.c,v 1.2 1995/10/09 11:19:47 roberto Exp $";*/
+void sm_dopr();
+char *DoprEnd;
+int SnprfOverflow;
+
+#if !HASSNPRINTF
+
+/* VARARGS3 */
+int
+# ifdef __STDC__
+snprintf(char *str, size_t count, const char *fmt, ...)
+# else
+snprintf(str, count, fmt, va_alist)
+ char *str;
+ size_t count;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ int len;
+ VA_LOCAL_DECL
+
+ VA_START(fmt);
+ len = vsnprintf(str, count, fmt, ap);
+ VA_END;
+ return len;
+}
+
+
+# ifndef luna2
+int
+vsnprintf(str, count, fmt, args)
+ char *str;
+ size_t count;
+ const char *fmt;
+ va_list args;
+{
+ str[0] = 0;
+ DoprEnd = str + count - 1;
+ SnprfOverflow = 0;
+ sm_dopr( str, fmt, args );
+ if (count > 0)
+ DoprEnd[0] = 0;
+ if (SnprfOverflow && tTd(57, 2))
+ printf("\nvsnprintf overflow, len = %ld, str = %s",
+ (long) count, shortenstring(str, MAXSHORTSTR));
+ return strlen(str);
+}
+
+# endif /* !luna2 */
+#endif /* !HASSNPRINTF */
+
+/*
+ * sm_dopr(): poor man's version of doprintf
+ */
+
+void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth));
+void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad));
+void dostr __P(( char * , int ));
+char *output;
+void dopr_outch __P(( int c ));
+int SyslogErrno;
+
+void
+sm_dopr( buffer, format, args )
+ char *buffer;
+ const char *format;
+ va_list args;
+{
+ int ch;
+ long value;
+ int longflag = 0;
+ int pointflag = 0;
+ int maxwidth = 0;
+ char *strvalue;
+ int ljust;
+ int len;
+ int zpad;
+# if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+# endif
+
+
+ output = buffer;
+ while( (ch = *format++) != '\0' ){
+ switch( ch ){
+ case '%':
+ ljust = len = zpad = maxwidth = 0;
+ longflag = pointflag = 0;
+ nextch:
+ ch = *format++;
+ switch( ch ){
+ case 0:
+ dostr( "**end of format**" , 0);
+ return;
+ case '-': ljust = 1; goto nextch;
+ case '0': /* set zero padding if len not set */
+ if(len==0 && !pointflag) zpad = '0';
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ if (pointflag)
+ maxwidth = maxwidth*10 + ch - '0';
+ else
+ len = len*10 + ch - '0';
+ goto nextch;
+ case '*':
+ if (pointflag)
+ maxwidth = va_arg( args, int );
+ else
+ len = va_arg( args, int );
+ goto nextch;
+ case '.': pointflag = 1; goto nextch;
+ case 'l': longflag = 1; goto nextch;
+ case 'u': case 'U':
+ /*fmtnum(value,base,dosign,ljust,len,zpad) */
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value, 10,0, ljust, len, zpad ); break;
+ case 'o': case 'O':
+ /*fmtnum(value,base,dosign,ljust,len,zpad) */
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value, 8,0, ljust, len, zpad ); break;
+ case 'd': case 'D':
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value, 10,1, ljust, len, zpad ); break;
+ case 'x':
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value, 16,0, ljust, len, zpad ); break;
+ case 'X':
+ if( longflag ){
+ value = va_arg( args, long );
+ } else {
+ value = va_arg( args, int );
+ }
+ fmtnum( value,-16,0, ljust, len, zpad ); break;
+ case 's':
+ strvalue = va_arg( args, char *);
+ if (maxwidth > 0 || !pointflag) {
+ if (pointflag && len > maxwidth)
+ len = maxwidth; /* Adjust padding */
+ fmtstr( strvalue,ljust,len,zpad, maxwidth);
+ }
+ break;
+ case 'c':
+ ch = va_arg( args, int );
+ dopr_outch( ch ); break;
+ case 'm':
+#if HASSTRERROR
+ dostr(strerror(SyslogErrno), 0);
+#else
+ if (SyslogErrno < 0 || SyslogErrno >= sys_nerr)
+ {
+ dostr("Error ", 0);
+ fmtnum(SyslogErrno, 10, 0, 0, 0, 0);
+ }
+ else
+ dostr((char *)sys_errlist[SyslogErrno], 0);
+#endif
+ break;
+
+ case '%': dopr_outch( ch ); continue;
+ default:
+ dostr( "???????" , 0);
+ }
+ break;
+ default:
+ dopr_outch( ch );
+ break;
+ }
+ }
+ *output = 0;
+}
+
+void
+fmtstr( value, ljust, len, zpad, maxwidth )
+ char *value;
+ int ljust, len, zpad, maxwidth;
+{
+ int padlen, strlen; /* amount to pad */
+
+ if( value == 0 ){
+ value = "<NULL>";
+ }
+ for( strlen = 0; value[strlen]; ++ strlen ); /* strlen */
+ if (strlen > maxwidth && maxwidth)
+ strlen = maxwidth;
+ padlen = len - strlen;
+ if( padlen < 0 ) padlen = 0;
+ if( ljust ) padlen = -padlen;
+ while( padlen > 0 ) {
+ dopr_outch( ' ' );
+ --padlen;
+ }
+ dostr( value, maxwidth );
+ while( padlen < 0 ) {
+ dopr_outch( ' ' );
+ ++padlen;
+ }
+}
+
+void
+fmtnum( value, base, dosign, ljust, len, zpad )
+ long value;
+ int base, dosign, ljust, len, zpad;
+{
+ int signvalue = 0;
+ unsigned long uvalue;
+ char convert[20];
+ int place = 0;
+ int padlen = 0; /* amount to pad */
+ int caps = 0;
+
+ /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n",
+ value, base, dosign, ljust, len, zpad )); */
+ uvalue = value;
+ if( dosign ){
+ if( value < 0 ) {
+ signvalue = '-';
+ uvalue = -value;
+ }
+ }
+ if( base < 0 ){
+ caps = 1;
+ base = -base;
+ }
+ do{
+ convert[place++] =
+ (caps? "0123456789ABCDEF":"0123456789abcdef")
+ [uvalue % (unsigned)base ];
+ uvalue = (uvalue / (unsigned)base );
+ }while(uvalue);
+ convert[place] = 0;
+ padlen = len - place;
+ if( padlen < 0 ) padlen = 0;
+ if( ljust ) padlen = -padlen;
+ /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n",
+ convert,place,signvalue,padlen)); */
+ if( zpad && padlen > 0 ){
+ if( signvalue ){
+ dopr_outch( signvalue );
+ --padlen;
+ signvalue = 0;
+ }
+ while( padlen > 0 ){
+ dopr_outch( zpad );
+ --padlen;
+ }
+ }
+ while( padlen > 0 ) {
+ dopr_outch( ' ' );
+ --padlen;
+ }
+ if( signvalue ) dopr_outch( signvalue );
+ while( place > 0 ) dopr_outch( convert[--place] );
+ while( padlen < 0 ){
+ dopr_outch( ' ' );
+ ++padlen;
+ }
+}
+
+void
+dostr( str , cut)
+ char *str;
+ int cut;
+{
+ if (cut) {
+ while(*str && cut-- > 0) dopr_outch(*str++);
+ } else {
+ while(*str) dopr_outch(*str++);
+ }
+}
+
+void
+dopr_outch( c )
+ int c;
+{
+#if 0
+ if( iscntrl(c) && c != '\n' && c != '\t' ){
+ c = '@' + (c & 0x1F);
+ if( DoprEnd == 0 || output < DoprEnd )
+ *output++ = '^';
+ }
+#endif
+ if( DoprEnd == 0 || output < DoprEnd )
+ *output++ = c;
+ else
+ SnprfOverflow++;
+}
+
+ /*
+** QUAD_TO_STRING -- Convert a quad type to a string.
+**
+** Convert a quad type to a string. This must be done
+** separately as %lld/%qd are not supported by snprint()
+** and adding support would slow down systems which only
+** emulate the data type.
+**
+** Parameters:
+** value -- number to convert to a string.
+**
+** Returns:
+** pointer to a string.
+*/
+
+char *
+quad_to_string(value)
+ QUAD_T value;
+{
+ char *fmtstr;
+ static char buf[64];
+
+ /*
+ ** Use sprintf() instead of snprintf() since snprintf()
+ ** does not support %qu or %llu. The buffer is large enough
+ ** to hold the string so there is no danger of buffer
+ ** overflow.
+ */
+
+#if NEED_PERCENTQ
+ fmtstr = "%qu";
+#else
+ fmtstr = "%llu";
+#endif
+ sprintf(buf, fmtstr, value);
+ return buf;
+}
+ /*
+** SHORTENSTRING -- return short version of a string
+**
+** If the string is already short, just return it. If it is too
+** long, return the head and tail of the string.
+**
+** Parameters:
+** s -- the string to shorten.
+** m -- the max length of the string.
+**
+** Returns:
+** Either s or a short version of s.
+*/
+
+char *
+shortenstring(s, m)
+ register const char *s;
+ int m;
+{
+ int l;
+ static char buf[MAXSHORTSTR + 1];
+
+ l = strlen(s);
+ if (l < m)
+ return (char *) s;
+ if (m > MAXSHORTSTR)
+ m = MAXSHORTSTR;
+ else if (m < 10)
+ {
+ if (m < 5)
+ {
+ strncpy(buf, s, m);
+ buf[m] = '\0';
+ return buf;
+ }
+ strncpy(buf, s, m - 3);
+ strcpy(buf + m - 3, "...");
+ return buf;
+ }
+ m = (m - 3) / 2;
+ strncpy(buf, s, m);
+ strcpy(buf + m, "...");
+ strcpy(buf + m + 3, s + l - m);
+ return buf;
+}
diff --git a/contrib/sendmail/src/srvrsmtp.c b/contrib/sendmail/src/srvrsmtp.c
new file mode 100644
index 0000000..fba103c
--- /dev/null
+++ b/contrib/sendmail/src/srvrsmtp.c
@@ -0,0 +1,1532 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+# include "sendmail.h"
+
+#ifndef lint
+#if SMTP
+static char sccsid[] = "@(#)srvrsmtp.c 8.181 (Berkeley) 6/15/98 (with SMTP)";
+#else
+static char sccsid[] = "@(#)srvrsmtp.c 8.181 (Berkeley) 6/15/98 (without SMTP)";
+#endif
+#endif /* not lint */
+
+# include <errno.h>
+
+# if SMTP
+
+/*
+** SMTP -- run the SMTP protocol.
+**
+** Parameters:
+** nullserver -- if non-NULL, rejection message for
+** all SMTP commands.
+** e -- the envelope.
+**
+** Returns:
+** never.
+**
+** Side Effects:
+** Reads commands from the input channel and processes
+** them.
+*/
+
+struct cmd
+{
+ char *cmdname; /* command name */
+ int cmdcode; /* internal code, see below */
+};
+
+/* values for cmdcode */
+# define CMDERROR 0 /* bad command */
+# define CMDMAIL 1 /* mail -- designate sender */
+# define CMDRCPT 2 /* rcpt -- designate recipient */
+# define CMDDATA 3 /* data -- send message text */
+# define CMDRSET 4 /* rset -- reset state */
+# define CMDVRFY 5 /* vrfy -- verify address */
+# define CMDEXPN 6 /* expn -- expand address */
+# define CMDNOOP 7 /* noop -- do nothing */
+# define CMDQUIT 8 /* quit -- close connection and die */
+# define CMDHELO 9 /* helo -- be polite */
+# define CMDHELP 10 /* help -- give usage info */
+# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
+# define CMDETRN 12 /* etrn -- flush queue */
+/* non-standard commands */
+# define CMDONEX 16 /* onex -- sending one transaction only */
+# define CMDVERB 17 /* verb -- go into verbose mode */
+# define CMDXUSR 18 /* xusr -- initial (user) submission */
+/* use this to catch and log "door handle" attempts on your system */
+# define CMDLOGBOGUS 23 /* bogus command that should be logged */
+/* debugging-only commands, only enabled if SMTPDEBUG is defined */
+# define CMDDBGQSHOW 24 /* showq -- show send queue */
+# define CMDDBGDEBUG 25 /* debug -- set debug mode */
+
+static struct cmd CmdTab[] =
+{
+ { "mail", CMDMAIL },
+ { "rcpt", CMDRCPT },
+ { "data", CMDDATA },
+ { "rset", CMDRSET },
+ { "vrfy", CMDVRFY },
+ { "expn", CMDEXPN },
+ { "help", CMDHELP },
+ { "noop", CMDNOOP },
+ { "quit", CMDQUIT },
+ { "helo", CMDHELO },
+ { "ehlo", CMDEHLO },
+ { "etrn", CMDETRN },
+ { "verb", CMDVERB },
+ { "onex", CMDONEX },
+ { "xusr", CMDXUSR },
+ /* remaining commands are here only to trap and log attempts to use them */
+ { "showq", CMDDBGQSHOW },
+ { "debug", CMDDBGDEBUG },
+ { "wiz", CMDLOGBOGUS },
+
+ { NULL, CMDERROR }
+};
+
+bool OneXact = FALSE; /* one xaction only this run */
+char *CurSmtpClient; /* who's at the other end of channel */
+
+static char *skipword __P((char *volatile, char *));
+
+
+#define MAXBADCOMMANDS 25 /* maximum number of bad commands */
+#define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */
+#define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
+#define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
+#define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
+
+void
+smtp(nullserver, e)
+ char *nullserver;
+ register ENVELOPE *volatile e;
+{
+ register char *volatile p;
+ register struct cmd *c;
+ char *cmd;
+ auto ADDRESS *vrfyqueue;
+ ADDRESS *a;
+ volatile bool gotmail; /* mail command received */
+ volatile bool gothello; /* helo command received */
+ bool vrfy; /* set if this is a vrfy command */
+ char *volatile protocol; /* sending protocol */
+ char *volatile sendinghost; /* sending hostname */
+ char *volatile peerhostname; /* name of SMTP peer or "localhost" */
+ auto char *delimptr;
+ char *id;
+ volatile int nrcpts = 0; /* number of RCPT commands */
+ bool doublequeue;
+ bool discard;
+ volatile int badcommands = 0; /* count of bad commands */
+ volatile int nverifies = 0; /* count of VRFY/EXPN commands */
+ volatile int n_etrn = 0; /* count of ETRN commands */
+ volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */
+ volatile int n_helo = 0; /* count of HELO/EHLO commands */
+ bool ok;
+ volatile int lognullconnection = TRUE;
+ register char *q;
+ QUEUE_CHAR *new;
+ char inp[MAXLINE];
+ char cmdbuf[MAXLINE];
+ extern ENVELOPE BlankEnvelope;
+ extern void help __P((char *));
+ extern void settime __P((ENVELOPE *));
+ extern bool enoughdiskspace __P((long));
+ extern int runinchild __P((char *, ENVELOPE *));
+ extern void checksmtpattack __P((volatile int *, int, char *, ENVELOPE *));
+
+ if (fileno(OutChannel) != fileno(stdout))
+ {
+ /* arrange for debugging output to go to remote host */
+ (void) dup2(fileno(OutChannel), fileno(stdout));
+ }
+ settime(e);
+ peerhostname = RealHostName;
+ if (peerhostname == NULL)
+ peerhostname = "localhost";
+ CurHostName = peerhostname;
+ CurSmtpClient = macvalue('_', e);
+ if (CurSmtpClient == NULL)
+ CurSmtpClient = CurHostName;
+
+ /* check_relay may have set discard bit, save for later */
+ discard = bitset(EF_DISCARD, e->e_flags);
+
+ setproctitle("server %s startup", CurSmtpClient);
+#if DAEMON
+ if (LogLevel > 11)
+ {
+ /* log connection information */
+ sm_syslog(LOG_INFO, NOQID,
+ "SMTP connect from %.100s (%.100s)",
+ CurSmtpClient, anynet_ntoa(&RealHostAddr));
+ }
+#endif
+
+ /* output the first line, inserting "ESMTP" as second word */
+ expand(SmtpGreeting, inp, sizeof inp, e);
+ p = strchr(inp, '\n');
+ if (p != NULL)
+ *p++ = '\0';
+ id = strchr(inp, ' ');
+ if (id == NULL)
+ id = &inp[strlen(inp)];
+ cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
+ message(cmd, id - inp, inp, id);
+
+ /* output remaining lines */
+ while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
+ {
+ *p++ = '\0';
+ if (isascii(*id) && isspace(*id))
+ id++;
+ message("220-%s", id);
+ }
+ if (id != NULL)
+ {
+ if (isascii(*id) && isspace(*id))
+ id++;
+ message("220 %s", id);
+ }
+
+ protocol = NULL;
+ sendinghost = macvalue('s', e);
+ gothello = FALSE;
+ gotmail = FALSE;
+ for (;;)
+ {
+ /* arrange for backout */
+ (void) setjmp(TopFrame);
+ QuickAbort = FALSE;
+ HoldErrs = FALSE;
+ SuprErrs = FALSE;
+ LogUsrErrs = FALSE;
+ OnlyOneError = TRUE;
+ e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
+
+ /* setup for the read */
+ e->e_to = NULL;
+ Errors = 0;
+ (void) fflush(stdout);
+
+ /* read the input line */
+ SmtpPhase = "server cmd read";
+ setproctitle("server %s cmd read", CurSmtpClient);
+ p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
+ SmtpPhase);
+
+ /* handle errors */
+ if (p == NULL)
+ {
+ /* end of file, just die */
+ disconnect(1, e);
+ message("421 %s Lost input channel from %s",
+ MyHostName, CurSmtpClient);
+ if (LogLevel > (gotmail ? 1 : 19))
+ sm_syslog(LOG_NOTICE, e->e_id,
+ "lost input channel from %.100s",
+ CurSmtpClient);
+ if (lognullconnection && LogLevel > 5)
+ sm_syslog(LOG_INFO, NULL,
+ "Null connection from %.100s",
+ CurSmtpClient);
+
+ /*
+ ** If have not accepted mail (DATA), do not bounce
+ ** bad addresses back to sender.
+ */
+ if (bitset(EF_CLRQUEUE, e->e_flags))
+ e->e_sendqueue = NULL;
+
+ if (InChild)
+ ExitStat = EX_QUIT;
+ finis();
+ }
+
+ /* clean up end of line */
+ fixcrlf(inp, TRUE);
+
+ /* echo command to transcript */
+ if (e->e_xfp != NULL)
+ fprintf(e->e_xfp, "<<< %s\n", inp);
+
+ if (LogLevel >= 15)
+ sm_syslog(LOG_INFO, e->e_id,
+ "<-- %s",
+ inp);
+
+ if (e->e_id == NULL)
+ setproctitle("%s: %.80s", CurSmtpClient, inp);
+ else
+ setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
+
+ /* break off command */
+ for (p = inp; isascii(*p) && isspace(*p); p++)
+ continue;
+ cmd = cmdbuf;
+ while (*p != '\0' &&
+ !(isascii(*p) && isspace(*p)) &&
+ cmd < &cmdbuf[sizeof cmdbuf - 2])
+ *cmd++ = *p++;
+ *cmd = '\0';
+
+ /* throw away leading whitespace */
+ while (isascii(*p) && isspace(*p))
+ p++;
+
+ /* decode command */
+ for (c = CmdTab; c->cmdname != NULL; c++)
+ {
+ if (!strcasecmp(c->cmdname, cmdbuf))
+ break;
+ }
+
+ /* reset errors */
+ errno = 0;
+
+ /*
+ ** Process command.
+ **
+ ** If we are running as a null server, return 550
+ ** to everything.
+ */
+
+ if (nullserver != NULL)
+ {
+ switch (c->cmdcode)
+ {
+ case CMDQUIT:
+ case CMDHELO:
+ case CMDEHLO:
+ case CMDNOOP:
+ /* process normally */
+ break;
+
+ default:
+ if (++badcommands > MAXBADCOMMANDS)
+ sleep(1);
+ usrerr("550 %s", nullserver);
+ continue;
+ }
+ }
+
+ /* non-null server */
+ switch (c->cmdcode)
+ {
+ case CMDMAIL:
+ case CMDEXPN:
+ case CMDVRFY:
+ case CMDETRN:
+ lognullconnection = FALSE;
+ }
+
+ switch (c->cmdcode)
+ {
+ case CMDHELO: /* hello -- introduce yourself */
+ case CMDEHLO: /* extended hello */
+ if (c->cmdcode == CMDEHLO)
+ {
+ protocol = "ESMTP";
+ SmtpPhase = "server EHLO";
+ }
+ else
+ {
+ protocol = "SMTP";
+ SmtpPhase = "server HELO";
+ }
+
+ /* avoid denial-of-service */
+ checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e);
+
+ /* check for duplicate HELO/EHLO per RFC 1651 4.2 */
+ if (gothello)
+ {
+ usrerr("503 %s Duplicate HELO/EHLO",
+ MyHostName);
+ break;
+ }
+
+ /* check for valid domain name (re 1123 5.2.5) */
+ if (*p == '\0' && !AllowBogusHELO)
+ {
+ usrerr("501 %s requires domain address",
+ cmdbuf);
+ break;
+ }
+
+ /* check for long domain name (hides Received: info) */
+ if (strlen(p) > MAXNAME)
+ {
+ usrerr("501 Invalid domain name");
+ break;
+ }
+
+ for (q = p; *q != '\0'; q++)
+ {
+ if (!isascii(*q))
+ break;
+ if (isalnum(*q))
+ continue;
+ if (isspace(*q))
+ {
+ *q = '\0';
+ break;
+ }
+ if (strchr("[].-_#", *q) == NULL)
+ break;
+ }
+ if (*q == '\0')
+ {
+ q = "pleased to meet you";
+ sendinghost = newstr(p);
+ }
+ else if (!AllowBogusHELO)
+ {
+ usrerr("501 Invalid domain name");
+ break;
+ }
+ else
+ {
+ q = "accepting invalid domain name";
+ }
+
+ gothello = TRUE;
+
+ /* print HELO response message */
+ if (c->cmdcode != CMDEHLO || nullserver != NULL)
+ {
+ message("250 %s Hello %s, %s",
+ MyHostName, CurSmtpClient, q);
+ break;
+ }
+
+ message("250-%s Hello %s, %s",
+ MyHostName, CurSmtpClient, q);
+
+ /* print EHLO features list */
+ if (!bitset(PRIV_NOEXPN, PrivacyFlags))
+ {
+ message("250-EXPN");
+ if (!bitset(PRIV_NOVERB, PrivacyFlags))
+ message("250-VERB");
+ }
+#if MIME8TO7
+ message("250-8BITMIME");
+#endif
+ if (MaxMessageSize > 0)
+ message("250-SIZE %ld", MaxMessageSize);
+ else
+ message("250-SIZE");
+#if DSN
+ if (SendMIMEErrors)
+ message("250-DSN");
+#endif
+ message("250-ONEX");
+ if (!bitset(PRIV_NOETRN, PrivacyFlags))
+ message("250-ETRN");
+ message("250-XUSR");
+ message("250 HELP");
+ break;
+
+ case CMDMAIL: /* mail -- designate sender */
+ SmtpPhase = "server MAIL";
+
+ /* check for validity of this command */
+ if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
+ {
+ usrerr("503 Polite people say HELO first");
+ break;
+ }
+ if (gotmail)
+ {
+ usrerr("503 Sender already specified");
+ break;
+ }
+ if (InChild)
+ {
+ errno = 0;
+ syserr("503 Nested MAIL command: MAIL %s", p);
+ finis();
+ }
+
+ /* make sure we know who the sending host is */
+ if (sendinghost == NULL)
+ sendinghost = peerhostname;
+
+ p = skipword(p, "from");
+ if (p == NULL)
+ break;
+
+ /* fork a subprocess to process this command */
+ if (runinchild("SMTP-MAIL", e) > 0)
+ break;
+ if (Errors > 0)
+ goto undo_subproc_no_pm;
+ if (!gothello)
+ {
+ auth_warning(e,
+ "%s didn't use HELO protocol",
+ CurSmtpClient);
+ }
+#ifdef PICKY_HELO_CHECK
+ if (strcasecmp(sendinghost, peerhostname) != 0 &&
+ (strcasecmp(peerhostname, "localhost") != 0 ||
+ strcasecmp(sendinghost, MyHostName) != 0))
+ {
+ auth_warning(e, "Host %s claimed to be %s",
+ CurSmtpClient, sendinghost);
+ }
+#endif
+
+ if (protocol == NULL)
+ protocol = "SMTP";
+ define('r', protocol, e);
+ define('s', sendinghost, e);
+ initsys(e);
+ if (Errors > 0)
+ goto undo_subproc_no_pm;
+ nrcpts = 0;
+ e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
+ setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
+
+ /* child -- go do the processing */
+ if (setjmp(TopFrame) > 0)
+ {
+ /* this failed -- undo work */
+ undo_subproc_no_pm:
+ e->e_flags &= ~EF_PM_NOTIFY;
+ undo_subproc:
+ if (InChild)
+ {
+ QuickAbort = FALSE;
+ SuprErrs = TRUE;
+ e->e_flags &= ~EF_FATALERRS;
+ finis();
+ }
+ break;
+ }
+ QuickAbort = TRUE;
+
+ /* must parse sender first */
+ delimptr = NULL;
+ setsender(p, e, &delimptr, ' ', FALSE);
+ if (delimptr != NULL && *delimptr != '\0')
+ *delimptr++ = '\0';
+ if (Errors > 0)
+ goto undo_subproc_no_pm;
+
+ /* do config file checking of the sender */
+ if (rscheck("check_mail", p, NULL, e) != EX_OK ||
+ Errors > 0)
+ goto undo_subproc_no_pm;
+
+ /* check for possible spoofing */
+ if (RealUid != 0 && OpMode == MD_SMTP &&
+ !wordinclass(RealUserName, 't') &&
+ !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
+ strcmp(e->e_from.q_user, RealUserName) != 0)
+ {
+ auth_warning(e, "%s owned process doing -bs",
+ RealUserName);
+ }
+
+ /* now parse ESMTP arguments */
+ e->e_msgsize = 0;
+ p = delimptr;
+ while (p != NULL && *p != '\0')
+ {
+ char *kp;
+ char *vp = NULL;
+ extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
+
+ /* locate the beginning of the keyword */
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ kp = p;
+
+ /* skip to the value portion */
+ while ((isascii(*p) && isalnum(*p)) || *p == '-')
+ p++;
+ if (*p == '=')
+ {
+ *p++ = '\0';
+ vp = p;
+
+ /* skip to the end of the value */
+ while (*p != '\0' && *p != ' ' &&
+ !(isascii(*p) && iscntrl(*p)) &&
+ *p != '=')
+ p++;
+ }
+
+ if (*p != '\0')
+ *p++ = '\0';
+
+ if (tTd(19, 1))
+ printf("MAIL: got arg %s=\"%s\"\n", kp,
+ vp == NULL ? "<null>" : vp);
+
+ mail_esmtp_args(kp, vp, e);
+ if (Errors > 0)
+ goto undo_subproc_no_pm;
+ }
+ if (Errors > 0)
+ goto undo_subproc_no_pm;
+
+ if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
+ {
+ usrerr("552 Message size exceeds fixed maximum message size (%ld)",
+ MaxMessageSize);
+ goto undo_subproc_no_pm;
+ }
+
+ if (!enoughdiskspace(e->e_msgsize))
+ {
+ usrerr("452 Insufficient disk space; try again later");
+ goto undo_subproc_no_pm;
+ }
+ if (Errors > 0)
+ goto undo_subproc_no_pm;
+ message("250 Sender ok");
+ gotmail = TRUE;
+ break;
+
+ case CMDRCPT: /* rcpt -- designate recipient */
+ if (!gotmail)
+ {
+ usrerr("503 Need MAIL before RCPT");
+ break;
+ }
+ SmtpPhase = "server RCPT";
+ if (setjmp(TopFrame) > 0)
+ {
+ e->e_flags &= ~EF_FATALERRS;
+ break;
+ }
+ QuickAbort = TRUE;
+ LogUsrErrs = TRUE;
+
+ /* limit flooding of our machine */
+ if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg)
+ {
+ usrerr("452 Too many recipients");
+ break;
+ }
+
+ if (e->e_sendmode != SM_DELIVER)
+ e->e_flags |= EF_VRFYONLY;
+
+ p = skipword(p, "to");
+ if (p == NULL)
+ break;
+ a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
+ if (a == NULL || Errors > 0)
+ break;
+ if (delimptr != NULL && *delimptr != '\0')
+ *delimptr++ = '\0';
+
+ /* do config file checking of the recipient */
+ if (rscheck("check_rcpt", p, NULL, e) != EX_OK ||
+ Errors > 0)
+ break;
+
+ /* now parse ESMTP arguments */
+ p = delimptr;
+ while (p != NULL && *p != '\0')
+ {
+ char *kp;
+ char *vp = NULL;
+ extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
+
+ /* locate the beginning of the keyword */
+ while (isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ kp = p;
+
+ /* skip to the value portion */
+ while ((isascii(*p) && isalnum(*p)) || *p == '-')
+ p++;
+ if (*p == '=')
+ {
+ *p++ = '\0';
+ vp = p;
+
+ /* skip to the end of the value */
+ while (*p != '\0' && *p != ' ' &&
+ !(isascii(*p) && iscntrl(*p)) &&
+ *p != '=')
+ p++;
+ }
+
+ if (*p != '\0')
+ *p++ = '\0';
+
+ if (tTd(19, 1))
+ printf("RCPT: got arg %s=\"%s\"\n", kp,
+ vp == NULL ? "<null>" : vp);
+
+ rcpt_esmtp_args(a, kp, vp, e);
+ if (Errors > 0)
+ break;
+ }
+ if (Errors > 0)
+ break;
+
+ /* save in recipient list after ESMTP mods */
+ a = recipient(a, &e->e_sendqueue, 0, e);
+ if (Errors > 0)
+ break;
+
+ /* no errors during parsing, but might be a duplicate */
+ e->e_to = a->q_paddr;
+ if (!bitset(QBADADDR, a->q_flags))
+ {
+ message("250 Recipient ok%s",
+ bitset(QQUEUEUP, a->q_flags) ?
+ " (will queue)" : "");
+ nrcpts++;
+ }
+ else
+ {
+ /* punt -- should keep message in ADDRESS.... */
+ usrerr("550 Addressee unknown");
+ }
+ break;
+
+ case CMDDATA: /* data -- text of mail */
+ SmtpPhase = "server DATA";
+ if (!gotmail)
+ {
+ usrerr("503 Need MAIL command");
+ break;
+ }
+ else if (nrcpts <= 0)
+ {
+ usrerr("503 Need RCPT (recipient)");
+ break;
+ }
+
+ /* put back discard bit */
+ if (discard)
+ e->e_flags |= EF_DISCARD;
+
+ /* check to see if we need to re-expand aliases */
+ /* also reset QBADADDR on already-diagnosted addrs */
+ doublequeue = FALSE;
+ for (a = e->e_sendqueue; a != NULL; a = a->q_next)
+ {
+ if (bitset(QVERIFIED, a->q_flags) &&
+ !bitset(EF_DISCARD, e->e_flags))
+ {
+ /* need to re-expand aliases */
+ doublequeue = TRUE;
+ }
+ if (bitset(QBADADDR, a->q_flags))
+ {
+ /* make this "go away" */
+ a->q_flags |= QDONTSEND;
+ a->q_flags &= ~QBADADDR;
+ }
+ }
+
+ /* collect the text of the message */
+ SmtpPhase = "collect";
+ buffer_errors();
+ collect(InChannel, TRUE, NULL, e);
+ if (Errors > 0)
+ {
+ flush_errors(TRUE);
+ buffer_errors();
+ goto abortmessage;
+ }
+
+ /* make sure we actually do delivery */
+ e->e_flags &= ~EF_CLRQUEUE;
+
+ /* from now on, we have to operate silently */
+ buffer_errors();
+ e->e_errormode = EM_MAIL;
+
+ /*
+ ** Arrange to send to everyone.
+ ** If sending to multiple people, mail back
+ ** errors rather than reporting directly.
+ ** In any case, don't mail back errors for
+ ** anything that has happened up to
+ ** now (the other end will do this).
+ ** Truncate our transcript -- the mail has gotten
+ ** to us successfully, and if we have
+ ** to mail this back, it will be easier
+ ** on the reader.
+ ** Then send to everyone.
+ ** Finally give a reply code. If an error has
+ ** already been given, don't mail a
+ ** message back.
+ ** We goose error returns by clearing error bit.
+ */
+
+ SmtpPhase = "delivery";
+ e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
+ id = e->e_id;
+
+ if (doublequeue)
+ {
+ /* make sure it is in the queue */
+ queueup(e, FALSE);
+ }
+ else
+ {
+ /* send to all recipients */
+ sendall(e, SM_DEFAULT);
+ }
+ e->e_to = NULL;
+
+ /* issue success message */
+ message("250 %s Message accepted for delivery", id);
+
+ /* if we just queued, poke it */
+ if (doublequeue &&
+ e->e_sendmode != SM_QUEUE &&
+ e->e_sendmode != SM_DEFER)
+ {
+ CurrentLA = getla();
+
+ if (!shouldqueue(e->e_msgpriority, e->e_ctime))
+ {
+ extern pid_t dowork __P((char *, bool, bool, ENVELOPE *));
+
+ unlockqueue(e);
+ (void) dowork(id, TRUE, TRUE, e);
+ }
+ }
+
+ abortmessage:
+ /* if in a child, pop back to our parent */
+ if (InChild)
+ finis();
+
+ /* clean up a bit */
+ gotmail = FALSE;
+ dropenvelope(e, TRUE);
+ CurEnv = e = newenvelope(e, CurEnv);
+ e->e_flags = BlankEnvelope.e_flags;
+ break;
+
+ case CMDRSET: /* rset -- reset state */
+ if (tTd(94, 100))
+ message("451 Test failure");
+ else
+ message("250 Reset state");
+
+ /* arrange to ignore any current send list */
+ e->e_sendqueue = NULL;
+ e->e_flags |= EF_CLRQUEUE;
+ if (InChild)
+ finis();
+
+ /* clean up a bit */
+ gotmail = FALSE;
+ SuprErrs = TRUE;
+ dropenvelope(e, TRUE);
+ CurEnv = e = newenvelope(e, CurEnv);
+ break;
+
+ case CMDVRFY: /* vrfy -- verify address */
+ case CMDEXPN: /* expn -- expand address */
+ checksmtpattack(&nverifies, MAXVRFYCOMMANDS,
+ c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e);
+ vrfy = c->cmdcode == CMDVRFY;
+ if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
+ PrivacyFlags))
+ {
+ if (vrfy)
+ message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
+ else
+ message("502 Sorry, we do not allow this operation");
+ if (LogLevel > 5)
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s: %s [rejected]",
+ CurSmtpClient,
+ shortenstring(inp, MAXSHORTSTR));
+ break;
+ }
+ else if (!gothello &&
+ bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
+ PrivacyFlags))
+ {
+ usrerr("503 I demand that you introduce yourself first");
+ break;
+ }
+ if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
+ break;
+ if (Errors > 0)
+ goto undo_subproc;
+ if (LogLevel > 5)
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s: %s",
+ CurSmtpClient,
+ shortenstring(inp, MAXSHORTSTR));
+ if (setjmp(TopFrame) > 0)
+ goto undo_subproc;
+ QuickAbort = TRUE;
+ vrfyqueue = NULL;
+ if (vrfy)
+ e->e_flags |= EF_VRFYONLY;
+ while (*p != '\0' && isascii(*p) && isspace(*p))
+ p++;
+ if (*p == '\0')
+ {
+ usrerr("501 Argument required");
+ }
+ else
+ {
+ (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
+ }
+ if (Errors > 0)
+ goto undo_subproc;
+ if (vrfyqueue == NULL)
+ {
+ usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
+ }
+ while (vrfyqueue != NULL)
+ {
+ extern void printvrfyaddr __P((ADDRESS *, bool, bool));
+
+ a = vrfyqueue;
+ while ((a = a->q_next) != NULL &&
+ bitset(QDONTSEND|QBADADDR, a->q_flags))
+ continue;
+ if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
+ printvrfyaddr(vrfyqueue, a == NULL, vrfy);
+ vrfyqueue = vrfyqueue->q_next;
+ }
+ if (InChild)
+ finis();
+ break;
+
+ case CMDETRN: /* etrn -- force queue flush */
+ if (bitset(PRIV_NOETRN, PrivacyFlags))
+ {
+ message("502 Sorry, we do not allow this operation");
+ if (LogLevel > 5)
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s: %s [rejected]",
+ CurSmtpClient,
+ shortenstring(inp, MAXSHORTSTR));
+ break;
+ }
+
+ if (strlen(p) <= 0)
+ {
+ usrerr("500 Parameter required");
+ break;
+ }
+
+ /* crude way to avoid denial-of-service attacks */
+ checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e);
+
+ if (LogLevel > 5)
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s: ETRN %s",
+ CurSmtpClient,
+ shortenstring(p, MAXSHORTSTR));
+
+ id = p;
+ if (*id == '@')
+ id++;
+ else
+ *--id = '@';
+
+ if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
+ {
+ syserr("500 ETRN out of memory");
+ break;
+ }
+ new->queue_match = id;
+ new->queue_next = NULL;
+ QueueLimitRecipient = new;
+ ok = runqueue(TRUE, TRUE);
+ free(QueueLimitRecipient);
+ QueueLimitRecipient = NULL;
+ if (ok && Errors == 0)
+ message("250 Queuing for node %s started", p);
+ break;
+
+ case CMDHELP: /* help -- give user info */
+ help(p);
+ break;
+
+ case CMDNOOP: /* noop -- do nothing */
+ checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e);
+ message("250 OK");
+ break;
+
+ case CMDQUIT: /* quit -- leave mail */
+ message("221 %s closing connection", MyHostName);
+
+doquit:
+ /* arrange to ignore any current send list */
+ e->e_sendqueue = NULL;
+
+ /* avoid future 050 messages */
+ disconnect(1, e);
+
+ if (InChild)
+ ExitStat = EX_QUIT;
+ if (lognullconnection && LogLevel > 5)
+ sm_syslog(LOG_INFO, NULL,
+ "Null connection from %.100s",
+ CurSmtpClient);
+ finis();
+
+ case CMDVERB: /* set verbose mode */
+ if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
+ bitset(PRIV_NOVERB, PrivacyFlags))
+ {
+ /* this would give out the same info */
+ message("502 Verbose unavailable");
+ break;
+ }
+ checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e);
+ Verbose = 1;
+ e->e_sendmode = SM_DELIVER;
+ message("250 Verbose mode");
+ break;
+
+ case CMDONEX: /* doing one transaction only */
+ checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e);
+ OneXact = TRUE;
+ message("250 Only one transaction");
+ break;
+
+ case CMDXUSR: /* initial (user) submission */
+ checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e);
+ UserSubmission = TRUE;
+ message("250 Initial submission");
+ break;
+
+# if SMTPDEBUG
+ case CMDDBGQSHOW: /* show queues */
+ printf("Send Queue=");
+ printaddr(e->e_sendqueue, TRUE);
+ break;
+
+ case CMDDBGDEBUG: /* set debug mode */
+ tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
+ tTflag(p);
+ message("200 Debug set");
+ break;
+
+# else /* not SMTPDEBUG */
+ case CMDDBGQSHOW: /* show queues */
+ case CMDDBGDEBUG: /* set debug mode */
+# endif /* SMTPDEBUG */
+ case CMDLOGBOGUS: /* bogus command */
+ if (LogLevel > 0)
+ sm_syslog(LOG_CRIT, e->e_id,
+ "\"%s\" command from %.100s (%.100s)",
+ c->cmdname, CurSmtpClient,
+ anynet_ntoa(&RealHostAddr));
+ /* FALL THROUGH */
+
+ case CMDERROR: /* unknown command */
+ if (++badcommands > MAXBADCOMMANDS)
+ {
+ message("421 %s Too many bad commands; closing connection",
+ MyHostName);
+ goto doquit;
+ }
+
+ usrerr("500 Command unrecognized: \"%s\"",
+ shortenstring(inp, MAXSHORTSTR));
+ break;
+
+ default:
+ errno = 0;
+ syserr("500 smtp: unknown code %d", c->cmdcode);
+ break;
+ }
+ }
+}
+ /*
+** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
+**
+** Parameters:
+** pcounter -- pointer to a counter for this command.
+** maxcount -- maximum value for this counter before we
+** slow down.
+** cname -- command name for logging.
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Slows down if we seem to be under attack.
+*/
+
+void
+checksmtpattack(pcounter, maxcount, cname, e)
+ volatile int *pcounter;
+ int maxcount;
+ char *cname;
+ ENVELOPE *e;
+{
+ if (++(*pcounter) >= maxcount)
+ {
+ if (*pcounter == maxcount && LogLevel > 5)
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s: %.40s attack?",
+ CurSmtpClient, cname);
+ }
+ sleep(*pcounter / maxcount);
+ }
+}
+ /*
+** SKIPWORD -- skip a fixed word.
+**
+** Parameters:
+** p -- place to start looking.
+** w -- word to skip.
+**
+** Returns:
+** p following w.
+** NULL on error.
+**
+** Side Effects:
+** clobbers the p data area.
+*/
+
+static char *
+skipword(p, w)
+ register char *volatile p;
+ char *w;
+{
+ register char *q;
+ char *firstp = p;
+
+ /* find beginning of word */
+ while (isascii(*p) && isspace(*p))
+ p++;
+ q = p;
+
+ /* find end of word */
+ while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
+ p++;
+ while (isascii(*p) && isspace(*p))
+ *p++ = '\0';
+ if (*p != ':')
+ {
+ syntax:
+ usrerr("501 Syntax error in parameters scanning \"%s\"",
+ shortenstring(firstp, MAXSHORTSTR));
+ return (NULL);
+ }
+ *p++ = '\0';
+ while (isascii(*p) && isspace(*p))
+ p++;
+
+ if (*p == '\0')
+ goto syntax;
+
+ /* see if the input word matches desired word */
+ if (strcasecmp(q, w))
+ goto syntax;
+
+ return (p);
+}
+ /*
+** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
+**
+** Parameters:
+** kp -- the parameter key.
+** vp -- the value of that parameter.
+** e -- the envelope.
+**
+** Returns:
+** none.
+*/
+
+void
+mail_esmtp_args(kp, vp, e)
+ char *kp;
+ char *vp;
+ ENVELOPE *e;
+{
+ if (strcasecmp(kp, "size") == 0)
+ {
+ if (vp == NULL)
+ {
+ usrerr("501 SIZE requires a value");
+ /* NOTREACHED */
+ }
+# if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
+ e->e_msgsize = strtoul(vp, (char **) NULL, 10);
+# else
+ e->e_msgsize = strtol(vp, (char **) NULL, 10);
+# endif
+ }
+ else if (strcasecmp(kp, "body") == 0)
+ {
+ if (vp == NULL)
+ {
+ usrerr("501 BODY requires a value");
+ /* NOTREACHED */
+ }
+ else if (strcasecmp(vp, "8bitmime") == 0)
+ {
+ SevenBitInput = FALSE;
+ }
+ else if (strcasecmp(vp, "7bit") == 0)
+ {
+ SevenBitInput = TRUE;
+ }
+ else
+ {
+ usrerr("501 Unknown BODY type %s",
+ vp);
+ /* NOTREACHED */
+ }
+ e->e_bodytype = newstr(vp);
+ }
+ else if (strcasecmp(kp, "envid") == 0)
+ {
+ if (vp == NULL)
+ {
+ usrerr("501 ENVID requires a value");
+ /* NOTREACHED */
+ }
+ if (!xtextok(vp))
+ {
+ usrerr("501 Syntax error in ENVID parameter value");
+ /* NOTREACHED */
+ }
+ if (e->e_envid != NULL)
+ {
+ usrerr("501 Duplicate ENVID parameter");
+ /* NOTREACHED */
+ }
+ e->e_envid = newstr(vp);
+ }
+ else if (strcasecmp(kp, "ret") == 0)
+ {
+ if (vp == NULL)
+ {
+ usrerr("501 RET requires a value");
+ /* NOTREACHED */
+ }
+ if (bitset(EF_RET_PARAM, e->e_flags))
+ {
+ usrerr("501 Duplicate RET parameter");
+ /* NOTREACHED */
+ }
+ e->e_flags |= EF_RET_PARAM;
+ if (strcasecmp(vp, "hdrs") == 0)
+ e->e_flags |= EF_NO_BODY_RETN;
+ else if (strcasecmp(vp, "full") != 0)
+ {
+ usrerr("501 Bad argument \"%s\" to RET", vp);
+ /* NOTREACHED */
+ }
+ }
+ else
+ {
+ usrerr("501 %s parameter unrecognized", kp);
+ /* NOTREACHED */
+ }
+}
+ /*
+** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
+**
+** Parameters:
+** a -- the address corresponding to the To: parameter.
+** kp -- the parameter key.
+** vp -- the value of that parameter.
+** e -- the envelope.
+**
+** Returns:
+** none.
+*/
+
+void
+rcpt_esmtp_args(a, kp, vp, e)
+ ADDRESS *a;
+ char *kp;
+ char *vp;
+ ENVELOPE *e;
+{
+ if (strcasecmp(kp, "notify") == 0)
+ {
+ char *p;
+
+ if (vp == NULL)
+ {
+ usrerr("501 NOTIFY requires a value");
+ /* NOTREACHED */
+ }
+ a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
+ a->q_flags |= QHASNOTIFY;
+ if (strcasecmp(vp, "never") == 0)
+ return;
+ for (p = vp; p != NULL; vp = p)
+ {
+ p = strchr(p, ',');
+ if (p != NULL)
+ *p++ = '\0';
+ if (strcasecmp(vp, "success") == 0)
+ a->q_flags |= QPINGONSUCCESS;
+ else if (strcasecmp(vp, "failure") == 0)
+ a->q_flags |= QPINGONFAILURE;
+ else if (strcasecmp(vp, "delay") == 0)
+ a->q_flags |= QPINGONDELAY;
+ else
+ {
+ usrerr("501 Bad argument \"%s\" to NOTIFY",
+ vp);
+ /* NOTREACHED */
+ }
+ }
+ }
+ else if (strcasecmp(kp, "orcpt") == 0)
+ {
+ if (vp == NULL)
+ {
+ usrerr("501 ORCPT requires a value");
+ /* NOTREACHED */
+ }
+ if (strchr(vp, ';') == NULL || !xtextok(vp))
+ {
+ usrerr("501 Syntax error in ORCPT parameter value");
+ /* NOTREACHED */
+ }
+ if (a->q_orcpt != NULL)
+ {
+ usrerr("501 Duplicate ORCPT parameter");
+ /* NOTREACHED */
+ }
+ a->q_orcpt = newstr(vp);
+ }
+ else
+ {
+ usrerr("501 %s parameter unrecognized", kp);
+ /* NOTREACHED */
+ }
+}
+ /*
+** PRINTVRFYADDR -- print an entry in the verify queue
+**
+** Parameters:
+** a -- the address to print
+** last -- set if this is the last one.
+** vrfy -- set if this is a VRFY command.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Prints the appropriate 250 codes.
+*/
+
+void
+printvrfyaddr(a, last, vrfy)
+ register ADDRESS *a;
+ bool last;
+ bool vrfy;
+{
+ char fmtbuf[20];
+
+ if (vrfy && a->q_mailer != NULL &&
+ !bitnset(M_VRFY250, a->q_mailer->m_flags))
+ strcpy(fmtbuf, "252");
+ else
+ strcpy(fmtbuf, "250");
+ fmtbuf[3] = last ? ' ' : '-';
+
+ if (a->q_fullname == NULL)
+ {
+ if (strchr(a->q_user, '@') == NULL)
+ strcpy(&fmtbuf[4], "<%s@%s>");
+ else
+ strcpy(&fmtbuf[4], "<%s>");
+ message(fmtbuf, a->q_user, MyHostName);
+ }
+ else
+ {
+ if (strchr(a->q_user, '@') == NULL)
+ strcpy(&fmtbuf[4], "%s <%s@%s>");
+ else
+ strcpy(&fmtbuf[4], "%s <%s>");
+ message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
+ }
+}
+ /*
+** RUNINCHILD -- return twice -- once in the child, then in the parent again
+**
+** Parameters:
+** label -- a string used in error messages
+**
+** Returns:
+** zero in the child
+** one in the parent
+**
+** Side Effects:
+** none.
+*/
+
+int
+runinchild(label, e)
+ char *label;
+ register ENVELOPE *e;
+{
+ pid_t childpid;
+
+ if (!OneXact)
+ {
+ /*
+ ** Disable child process reaping, in case ETRN has preceeded
+ ** MAIL command, and then fork.
+ */
+
+ (void) blocksignal(SIGCHLD);
+
+ childpid = dofork();
+ if (childpid < 0)
+ {
+ syserr("451 %s: cannot fork", label);
+ (void) releasesignal(SIGCHLD);
+ return (1);
+ }
+ if (childpid > 0)
+ {
+ auto int st;
+
+ /* parent -- wait for child to complete */
+ setproctitle("server %s child wait", CurSmtpClient);
+ st = waitfor(childpid);
+ if (st == -1)
+ syserr("451 %s: lost child", label);
+ else if (!WIFEXITED(st))
+ syserr("451 %s: died on signal %d",
+ label, st & 0177);
+
+ /* if we exited on a QUIT command, complete the process */
+ if (WEXITSTATUS(st) == EX_QUIT)
+ {
+ disconnect(1, e);
+ finis();
+ }
+
+ /* restore the child signal */
+ (void) releasesignal(SIGCHLD);
+
+ return (1);
+ }
+ else
+ {
+ /* child */
+ InChild = TRUE;
+ QuickAbort = FALSE;
+ clearenvelope(e, FALSE);
+ (void) setsignal(SIGCHLD, SIG_DFL);
+ (void) releasesignal(SIGCHLD);
+ }
+ }
+
+ /* open alias database */
+ initmaps(FALSE, e);
+
+ return (0);
+}
+
+# endif /* SMTP */
+ /*
+** HELP -- implement the HELP command.
+**
+** Parameters:
+** topic -- the topic we want help for.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** outputs the help file to message output.
+*/
+
+void
+help(topic)
+ char *topic;
+{
+ register FILE *hf;
+ int len;
+ bool noinfo;
+ int sff = SFF_OPENASROOT|SFF_REGONLY;
+ char buf[MAXLINE];
+ extern char Version[];
+
+ if (DontLockReadFiles)
+ sff |= SFF_NOLOCK;
+ if (!bitset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
+ sff |= SFF_SAFEDIRPATH;
+
+ if (HelpFile == NULL ||
+ (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
+ {
+ /* no help */
+ errno = 0;
+ message("502 Sendmail %s -- HELP not implemented", Version);
+ return;
+ }
+
+ if (topic == NULL || *topic == '\0')
+ {
+ topic = "smtp";
+ message("214-This is Sendmail version %s", Version);
+ noinfo = FALSE;
+ }
+ else
+ {
+ makelower(topic);
+ noinfo = TRUE;
+ }
+
+ len = strlen(topic);
+
+ while (fgets(buf, sizeof buf, hf) != NULL)
+ {
+ if (strncmp(buf, topic, len) == 0)
+ {
+ register char *p;
+
+ p = strchr(buf, '\t');
+ if (p == NULL)
+ p = buf;
+ else
+ p++;
+ fixcrlf(p, TRUE);
+ message("214-%s", p);
+ noinfo = FALSE;
+ }
+ }
+
+ if (noinfo)
+ message("504 HELP topic \"%.10s\" unknown", topic);
+ else
+ message("214 End of HELP info");
+ (void) fclose(hf);
+}
diff --git a/contrib/sendmail/src/stab.c b/contrib/sendmail/src/stab.c
new file mode 100644
index 0000000..1ffc7cb
--- /dev/null
+++ b/contrib/sendmail/src/stab.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)stab.c 8.19 (Berkeley) 5/19/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+** STAB -- manage the symbol table
+**
+** Parameters:
+** name -- the name to be looked up or inserted.
+** type -- the type of symbol.
+** op -- what to do:
+** ST_ENTER -- enter the name if not
+** already present.
+** ST_FIND -- find it only.
+**
+** Returns:
+** pointer to a STAB entry for this name.
+** NULL if not found and not entered.
+**
+** Side Effects:
+** can update the symbol table.
+*/
+
+# define STABSIZE 2003
+
+static STAB *SymTab[STABSIZE];
+
+STAB *
+stab(name, type, op)
+ char *name;
+ int type;
+ int op;
+{
+ register STAB *s;
+ register STAB **ps;
+ register int hfunc;
+ register char *p;
+ int len;
+ extern char lower __P((char));
+
+ if (tTd(36, 5))
+ printf("STAB: %s %d ", name, type);
+
+ /*
+ ** Compute the hashing function
+ */
+
+ hfunc = type;
+ for (p = name; *p != '\0'; p++)
+ hfunc = ((hfunc << 1) ^ (lower(*p) & 0377)) % STABSIZE;
+
+ if (tTd(36, 9))
+ printf("(hfunc=%d) ", hfunc);
+
+ ps = &SymTab[hfunc];
+ if (type == ST_MACRO || type == ST_RULESET)
+ {
+ while ((s = *ps) != NULL &&
+ (s->s_type != type || strcmp(name, s->s_name)))
+ ps = &s->s_next;
+ }
+ else
+ {
+ while ((s = *ps) != NULL &&
+ (s->s_type != type || strcasecmp(name, s->s_name)))
+ ps = &s->s_next;
+ }
+
+ /*
+ ** Dispose of the entry.
+ */
+
+ if (s != NULL || op == ST_FIND)
+ {
+ if (tTd(36, 5))
+ {
+ if (s == NULL)
+ printf("not found\n");
+ else
+ {
+ long *lp = (long *) s->s_class;
+
+ printf("type %d val %lx %lx %lx %lx\n",
+ s->s_type, lp[0], lp[1], lp[2], lp[3]);
+ }
+ }
+ return (s);
+ }
+
+ /*
+ ** Make a new entry and link it in.
+ */
+
+ if (tTd(36, 5))
+ printf("entered\n");
+
+ /* determine size of new entry */
+#if _FFR_MEMORY_MISER
+ switch (type)
+ {
+ case ST_CLASS:
+ len = sizeof s->s_class;
+ break;
+
+ case ST_ADDRESS:
+ len = sizeof s->s_address;
+ break;
+
+ case ST_MAILER:
+ len = sizeof s->s_mailer;
+
+ case ST_ALIAS:
+ len = sizeof s->s_alias;
+ break;
+
+ case ST_MAPCLASS:
+ len = sizeof s->s_mapclass;
+ break;
+
+ case ST_MAP:
+ len = sizeof s->s_map;
+ break;
+
+ case ST_HOSTSIG:
+ len = sizeof s->s_hostsig;
+ break;
+
+ case ST_NAMECANON:
+ len = sizeof s->s_namecanon;
+ break;
+
+ case ST_MACRO:
+ len = sizeof s->s_macro;
+ break;
+
+ case ST_RULESET:
+ len = sizeof s->s_ruleset;
+ break;
+
+ case ST_SERVICE:
+ len = sizeof s->s_service;
+ break;
+
+ case ST_HEADER:
+ len = sizeof s->s_header;
+ break;
+
+ default:
+ if (type >= ST_MCI)
+ len = sizeof s->s_mci;
+ else
+ {
+ syserr("stab: unknown symbol type %d", type);
+ len = sizeof s->s_value;
+ }
+ break;
+ }
+ len += sizeof *s - sizeof s->s_value;
+#else
+ len = sizeof *s;
+#endif
+
+ /* make new entry */
+ s = (STAB *) xalloc(len);
+ bzero((char *) s, len);
+ s->s_name = newstr(name);
+ s->s_type = type;
+ s->s_len = len;
+
+ /* link it in */
+ *ps = s;
+
+ return (s);
+}
+ /*
+** STABAPPLY -- apply function to all stab entries
+**
+** Parameters:
+** func -- the function to apply. It will be given one
+** parameter (the stab entry).
+** arg -- an arbitrary argument, passed to func.
+**
+** Returns:
+** none.
+*/
+
+void
+stabapply(func, arg)
+ void (*func)__P((STAB *, int));
+ int arg;
+{
+ register STAB **shead;
+ register STAB *s;
+
+ for (shead = SymTab; shead < &SymTab[STABSIZE]; shead++)
+ {
+ for (s = *shead; s != NULL; s = s->s_next)
+ {
+ if (tTd(36, 90))
+ printf("stabapply: trying %d/%s\n",
+ s->s_type, s->s_name);
+ func(s, arg);
+ }
+ }
+}
diff --git a/contrib/sendmail/src/stats.c b/contrib/sendmail/src/stats.c
new file mode 100644
index 0000000..767a55e
--- /dev/null
+++ b/contrib/sendmail/src/stats.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)stats.c 8.22 (Berkeley) 5/19/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include "mailstats.h"
+
+struct statistics Stat;
+
+bool GotStats = FALSE; /* set when we have stats to merge */
+
+#define ONE_K 1000 /* one thousand (twenty-four?) */
+#define KBYTES(x) (((x) + (ONE_K - 1)) / ONE_K)
+ /*
+** MARKSTATS -- mark statistics
+*/
+
+void
+markstats(e, to, reject)
+ register ENVELOPE *e;
+ register ADDRESS *to;
+ bool reject;
+{
+ if (reject == TRUE)
+ {
+ if (e->e_from.q_mailer != NULL)
+ {
+ if (bitset(EF_DISCARD, e->e_flags))
+ Stat.stat_nd[e->e_from.q_mailer->m_mno]++;
+ else
+ Stat.stat_nr[e->e_from.q_mailer->m_mno]++;
+ }
+ }
+ else if (to == NULL)
+ {
+ if (e->e_from.q_mailer != NULL)
+ {
+ Stat.stat_nf[e->e_from.q_mailer->m_mno]++;
+ Stat.stat_bf[e->e_from.q_mailer->m_mno] +=
+ KBYTES(e->e_msgsize);
+ }
+ }
+ else
+ {
+ Stat.stat_nt[to->q_mailer->m_mno]++;
+ Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(e->e_msgsize);
+ }
+ GotStats = TRUE;
+}
+ /*
+** POSTSTATS -- post statistics in the statistics file
+**
+** Parameters:
+** sfile -- the name of the statistics file.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** merges the Stat structure with the sfile file.
+*/
+
+void
+poststats(sfile)
+ char *sfile;
+{
+ register int fd;
+ int sff = SFF_REGONLY|SFF_OPENASROOT;
+ struct statistics stat;
+ extern off_t lseek();
+
+ if (sfile == NULL || !GotStats)
+ return;
+
+ (void) time(&Stat.stat_itime);
+ Stat.stat_size = sizeof Stat;
+ Stat.stat_magic = STAT_MAGIC;
+ Stat.stat_version = STAT_VERSION;
+
+ if (!bitset(DBS_WRITESTATSTOSYMLINK, DontBlameSendmail))
+ sff |= SFF_NOSLINK;
+ if (!bitset(DBS_WRITESTATSTOHARDLINK, DontBlameSendmail))
+ sff |= SFF_NOHLINK;
+
+ fd = safeopen(sfile, O_RDWR, 0644, sff);
+ if (fd < 0)
+ {
+ if (LogLevel > 12)
+ sm_syslog(LOG_INFO, NOQID, "poststats: %s: %s",
+ sfile, errstring(errno));
+ errno = 0;
+ return;
+ }
+ if (read(fd, (char *) &stat, sizeof stat) == sizeof stat &&
+ stat.stat_size == sizeof stat &&
+ stat.stat_magic == Stat.stat_magic &&
+ stat.stat_version == Stat.stat_version)
+ {
+ /* merge current statistics into statfile */
+ register int i;
+
+ for (i = 0; i < MAXMAILERS; i++)
+ {
+ stat.stat_nf[i] += Stat.stat_nf[i];
+ stat.stat_bf[i] += Stat.stat_bf[i];
+ stat.stat_nt[i] += Stat.stat_nt[i];
+ stat.stat_bt[i] += Stat.stat_bt[i];
+ stat.stat_nr[i] += Stat.stat_nr[i];
+ stat.stat_nd[i] += Stat.stat_nd[i];
+ }
+ }
+ else
+ bcopy((char *) &Stat, (char *) &stat, sizeof stat);
+
+ /* write out results */
+ (void) lseek(fd, (off_t) 0, 0);
+ (void) write(fd, (char *) &stat, sizeof stat);
+ (void) close(fd);
+
+ /* clear the structure to avoid future disappointment */
+ bzero(&Stat, sizeof stat);
+ GotStats = FALSE;
+}
diff --git a/contrib/sendmail/src/sysexits.c b/contrib/sendmail/src/sysexits.c
new file mode 100644
index 0000000..10d7c85
--- /dev/null
+++ b/contrib/sendmail/src/sysexits.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sysexits.c 8.13 (Berkeley) 5/24/98";
+#endif /* not lint */
+
+#include "sendmail.h"
+
+/*
+** SYSEXITS.C -- error messages corresponding to sysexits.h
+**
+** If the first character of the string is a colon, interpolate
+** the current errno after the rest of the string.
+*/
+
+char *SysExMsg[] =
+{
+ /* 64 USAGE */ " 500 Bad usage",
+ /* 65 DATAERR */ " 501 Data format error",
+ /* 66 NOINPUT */ ":550 Cannot open input",
+ /* 67 NOUSER */ " 550 User unknown",
+ /* 68 NOHOST */ " 550 Host unknown",
+ /* 69 UNAVAILABLE */ " 554 Service unavailable",
+ /* 70 SOFTWARE */ ":554 Internal error",
+ /* 71 OSERR */ ":451 Operating system error",
+ /* 72 OSFILE */ ":554 System file missing",
+ /* 73 CANTCREAT */ ":550 Can't create output",
+ /* 74 IOERR */ ":451 I/O error",
+ /* 75 TEMPFAIL */ " 250 Deferred",
+ /* 76 PROTOCOL */ " 554 Remote protocol error",
+ /* 77 NOPERM */ ":550 Insufficient permission",
+ /* 78 CONFIG */ " 554 Local configuration error",
+};
+
+int N_SysEx = sizeof(SysExMsg) / sizeof(SysExMsg[0]);
+ /*
+** DSNTOEXITSTAT -- convert DSN-style error code to EX_ style.
+**
+** Parameters:
+** dsncode -- the text of the DSN-style code.
+**
+** Returns:
+** The corresponding exit status.
+*/
+
+int
+dsntoexitstat(dsncode)
+ char *dsncode;
+{
+ int code2, code3;
+
+ /* first the easy cases.... */
+ if (*dsncode == '2')
+ return EX_OK;
+ if (*dsncode == '4')
+ return EX_TEMPFAIL;
+
+ /* now decode the other two field parts */
+ if (*++dsncode == '.')
+ dsncode++;
+ code2 = atoi(dsncode);
+ while (*dsncode != '\0' && *dsncode != '.')
+ dsncode++;
+ if (*dsncode != '\0')
+ dsncode++;
+ code3 = atoi(dsncode);
+
+ /* and do a nested switch to work them out */
+ switch (code2)
+ {
+ case 0: /* Other or Undefined status */
+ return EX_UNAVAILABLE;
+
+ case 1: /* Address Status */
+ switch (code3)
+ {
+ case 0: /* Other Address Status */
+ return EX_DATAERR;
+
+ case 1: /* Bad destination mailbox address */
+ case 6: /* Mailbox has moved, No forwarding address */
+ return EX_NOUSER;
+
+ case 2: /* Bad destination system address */
+ case 8: /* Bad senders system address */
+ return EX_NOHOST;
+
+ case 3: /* Bad destination mailbox address syntax */
+ case 7: /* Bad senders mailbox address syntax */
+ return EX_USAGE;
+
+ case 4: /* Destination mailbox address ambiguous */
+ return EX_UNAVAILABLE;
+
+ case 5: /* Destination address valid */
+ return EX_OK;
+ }
+ break;
+
+ case 2: /* Mailbox Status */
+ switch (code3)
+ {
+ case 0: /* Other or Undefined mailbox status */
+ case 1: /* Mailbox disabled, not acccepting messages */
+ case 2: /* Mailbox full */
+ case 4: /* Mailing list expansion problem */
+ return EX_UNAVAILABLE;
+
+ case 3: /* Message length exceeds administrative lim */
+ return EX_DATAERR;
+ }
+ break;
+
+ case 3: /* System Status */
+ return EX_OSERR;
+
+ case 4: /* Network and Routing Status */
+ switch (code3)
+ {
+ case 0: /* Other or undefined network or routing stat */
+ return EX_IOERR;
+
+ case 1: /* No answer from host */
+ case 3: /* Routing server failure */
+ case 5: /* Network congestion */
+ return EX_TEMPFAIL;
+
+ case 2: /* Bad connection */
+ return EX_IOERR;
+
+ case 4: /* Unable to route */
+ return EX_PROTOCOL;
+
+ case 6: /* Routing loop detected */
+ return EX_CONFIG;
+
+ case 7: /* Delivery time expired */
+ return EX_UNAVAILABLE;
+ }
+ break;
+
+ case 5: /* Protocol Status */
+ return EX_PROTOCOL;
+
+ case 6: /* Message Content or Media Status */
+ return EX_UNAVAILABLE;
+
+ case 7: /* Security Status */
+ return EX_DATAERR;
+ }
+ return EX_CONFIG;
+}
diff --git a/contrib/sendmail/src/trace.c b/contrib/sendmail/src/trace.c
new file mode 100644
index 0000000..5ff9795
--- /dev/null
+++ b/contrib/sendmail/src/trace.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)trace.c 8.12 (Berkeley) 5/19/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+** TtSETUP -- set up for trace package.
+**
+** Parameters:
+** vect -- pointer to trace vector.
+** size -- number of flags in trace vector.
+** defflags -- flags to set if no value given.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** environment is set up.
+*/
+
+u_char *tTvect;
+int tTsize;
+static char *DefFlags;
+
+void
+tTsetup(vect, size, defflags)
+ u_char *vect;
+ int size;
+ char *defflags;
+{
+ tTvect = vect;
+ tTsize = size;
+ DefFlags = defflags;
+}
+ /*
+** TtFLAG -- process an external trace flag description.
+**
+** Parameters:
+** s -- the trace flag.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** sets/clears trace flags.
+*/
+
+void
+tTflag(s)
+ register char *s;
+{
+ unsigned int first, last;
+ register unsigned int i;
+
+ if (*s == '\0')
+ s = DefFlags;
+
+ for (;;)
+ {
+ /* find first flag to set */
+ i = 0;
+ while (isascii(*s) && isdigit(*s))
+ i = i * 10 + (*s++ - '0');
+ first = i;
+
+ /* find last flag to set */
+ if (*s == '-')
+ {
+ i = 0;
+ while (isascii(*++s) && isdigit(*s))
+ i = i * 10 + (*s - '0');
+ }
+ last = i;
+
+ /* find the level to set it to */
+ i = 1;
+ if (*s == '.')
+ {
+ i = 0;
+ while (isascii(*++s) && isdigit(*s))
+ i = i * 10 + (*s - '0');
+ }
+
+ /* clean up args */
+ if (first >= tTsize)
+ first = tTsize - 1;
+ if (last >= tTsize)
+ last = tTsize - 1;
+
+ /* set the flags */
+ while (first <= last)
+ tTvect[first++] = i;
+
+ /* more arguments? */
+ if (*s++ == '\0')
+ return;
+ }
+}
diff --git a/contrib/sendmail/src/udb.c b/contrib/sendmail/src/udb.c
new file mode 100644
index 0000000..e7f42af
--- /dev/null
+++ b/contrib/sendmail/src/udb.c
@@ -0,0 +1,1264 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#include "sendmail.h"
+
+#ifndef lint
+#if USERDB
+static char sccsid [] = "@(#)udb.c 8.66 (Berkeley) 6/18/98 (with USERDB)";
+#else
+static char sccsid [] = "@(#)udb.c 8.66 (Berkeley) 6/18/98 (without USERDB)";
+#endif
+#endif
+
+#if USERDB
+
+#include <errno.h>
+
+#ifdef NEWDB
+# include <db.h>
+# ifndef DB_VERSION_MAJOR
+# define DB_VERSION_MAJOR 1
+# endif
+#else
+# define DBT struct _data_base_thang_
+DBT
+{
+ void *data; /* pointer to data */
+ size_t size; /* length of data */
+};
+#endif
+
+/*
+** UDB.C -- interface between sendmail and Berkeley User Data Base.
+**
+** This depends on the 4.4BSD db package.
+*/
+
+
+struct udbent
+{
+ char *udb_spec; /* string version of spec */
+ int udb_type; /* type of entry */
+ char *udb_default; /* default host for outgoing mail */
+ union
+ {
+ /* type UE_REMOTE -- do remote call for lookup */
+ struct
+ {
+ struct sockaddr_in _udb_addr; /* address */
+ int _udb_timeout; /* timeout */
+ } udb_remote;
+#define udb_addr udb_u.udb_remote._udb_addr
+#define udb_timeout udb_u.udb_remote._udb_timeout
+
+ /* type UE_FORWARD -- forward message to remote */
+ struct
+ {
+ char *_udb_fwdhost; /* name of forward host */
+ } udb_forward;
+#define udb_fwdhost udb_u.udb_forward._udb_fwdhost
+
+#ifdef NEWDB
+ /* type UE_FETCH -- lookup in local database */
+ struct
+ {
+ char *_udb_dbname; /* pathname of database */
+ DB *_udb_dbp; /* open database ptr */
+ } udb_lookup;
+#define udb_dbname udb_u.udb_lookup._udb_dbname
+#define udb_dbp udb_u.udb_lookup._udb_dbp
+#endif
+ } udb_u;
+};
+
+#define UDB_EOLIST 0 /* end of list */
+#define UDB_SKIP 1 /* skip this entry */
+#define UDB_REMOTE 2 /* look up in remote database */
+#define UDB_DBFETCH 3 /* look up in local database */
+#define UDB_FORWARD 4 /* forward to remote host */
+#define UDB_HESIOD 5 /* look up via hesiod */
+
+#define MAXUDBENT 10 /* maximum number of UDB entries */
+
+
+struct option
+{
+ char *name;
+ char *val;
+};
+
+#ifdef HESIOD
+extern int hes_udb_get __P((DBT *, DBT *));
+#endif
+extern int _udbx_init __P((ENVELOPE *));
+ /*
+** UDBEXPAND -- look up user in database and expand
+**
+** Parameters:
+** a -- address to expand.
+** sendq -- pointer to head of sendq to put the expansions in.
+** aliaslevel -- the current alias nesting depth.
+** e -- the current envelope.
+**
+** Returns:
+** EX_TEMPFAIL -- if something "odd" happened -- probably due
+** to accessing a file on an NFS server that is down.
+** EX_OK -- otherwise.
+**
+** Side Effects:
+** Modifies sendq.
+*/
+
+int UdbPort = 1616;
+int UdbTimeout = 10;
+
+struct udbent UdbEnts[MAXUDBENT + 1];
+int UdbSock = -1;
+bool UdbInitialized = FALSE;
+
+int
+udbexpand(a, sendq, aliaslevel, e)
+ register ADDRESS *a;
+ ADDRESS **sendq;
+ int aliaslevel;
+ register ENVELOPE *e;
+{
+ int i;
+ DBT key;
+ DBT info;
+ bool breakout;
+ register struct udbent *up;
+ int keylen;
+ int naddrs;
+ char keybuf[MAXKEY];
+
+ bzero(&key, sizeof key);
+ bzero(&info, sizeof info);
+
+ if (tTd(28, 1))
+ printf("udbexpand(%s)\n", a->q_paddr);
+
+ /* make certain we are supposed to send to this address */
+ if (bitset(QDONTSEND|QVERIFIED, a->q_flags))
+ return EX_OK;
+ e->e_to = a->q_paddr;
+
+ /* on first call, locate the database */
+ if (!UdbInitialized)
+ {
+ if (_udbx_init(e) == EX_TEMPFAIL)
+ return EX_TEMPFAIL;
+ }
+
+ /* short circuit the process if no chance of a match */
+ if (UdbSpec == NULL || UdbSpec[0] == '\0')
+ return EX_OK;
+
+ /* short circuit name begins with '\\' since it can't possibly match */
+ if (a->q_user[0] == '\\')
+ return EX_OK;
+
+ /* if name is too long, assume it won't match */
+ if (strlen(a->q_user) > (SIZE_T) sizeof keybuf - 12)
+ return EX_OK;
+
+ /* if name begins with a colon, it indicates our metadata */
+ if (a->q_user[0] == ':')
+ return EX_OK;
+
+ /* build actual database key */
+ (void) strcpy(keybuf, a->q_user);
+ (void) strcat(keybuf, ":maildrop");
+ keylen = strlen(keybuf);
+
+ breakout = FALSE;
+ for (up = UdbEnts; !breakout; up++)
+ {
+ char *user;
+ int usersize;
+ int userleft;
+ char userbuf[MEMCHUNKSIZE];
+#if defined(HESIOD) && defined(HES_GETMAILHOST)
+ char pobuf[MAXNAME];
+#endif
+#if defined(NEWDB) && DB_VERSION_MAJOR > 1
+ DBC *dbc = NULL;
+#endif
+
+ user = userbuf;
+ userbuf[0] = '\0';
+ usersize = sizeof userbuf;
+ userleft = sizeof userbuf - 1;
+
+ /*
+ ** Select action based on entry type.
+ **
+ ** On dropping out of this switch, "class" should
+ ** explain the type of the data, and "user" should
+ ** contain the user information.
+ */
+
+ switch (up->udb_type)
+ {
+#ifdef NEWDB
+ case UDB_DBFETCH:
+ key.data = keybuf;
+ key.size = keylen;
+ if (tTd(28, 80))
+ printf("udbexpand: trying %s (%d) via db\n",
+ keybuf, keylen);
+#if DB_VERSION_MAJOR < 2
+ i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
+#else
+ i = 0;
+ if (dbc == NULL &&
+ (errno = (*up->udb_dbp->cursor)(up->udb_dbp,
+ NULL, &dbc)) != 0)
+ i = -1;
+ if (i != 0 || dbc == NULL ||
+ (errno = dbc->c_get(dbc, &key,
+ &info, DB_SET)) != 0)
+ i = 1;
+#endif
+ if (i > 0 || info.size <= 0)
+ {
+ if (tTd(28, 2))
+ printf("udbexpand: no match on %s (%d)\n",
+ keybuf, keylen);
+#if DB_VERSION_MAJOR > 1
+ if (dbc != NULL)
+ {
+ (void) dbc->c_close(dbc);
+ dbc = NULL;
+ }
+#endif
+ break;
+ }
+ if (tTd(28, 80))
+ printf("udbexpand: match %.*s: %.*s\n",
+ (int) key.size, (char *) key.data,
+ (int) info.size, (char *) info.data);
+
+ a->q_flags &= ~QSELFREF;
+ while (i == 0 && key.size == keylen &&
+ bcmp(key.data, keybuf, keylen) == 0)
+ {
+ char *p;
+
+ if (bitset(EF_VRFYONLY, e->e_flags))
+ {
+ a->q_flags |= QVERIFIED;
+#if DB_VERSION_MAJOR > 1
+ if (dbc != NULL)
+ {
+ (void) dbc->c_close(dbc);
+ dbc = NULL;
+ }
+#endif
+ return EX_OK;
+ }
+
+ breakout = TRUE;
+ if (info.size >= userleft - 1)
+ {
+ char *nuser;
+ int size = MEMCHUNKSIZE;
+
+ if (info.size > MEMCHUNKSIZE)
+ size = info.size;
+ nuser = xalloc(usersize + size);
+
+ bcopy(user, nuser, usersize);
+ if (user != userbuf)
+ free(user);
+ user = nuser;
+ usersize += size;
+ userleft += size;
+ }
+ p = &user[strlen(user)];
+ if (p != user)
+ {
+ *p++ = ',';
+ userleft--;
+ }
+ bcopy(info.data, p, info.size);
+ p[info.size] = '\0';
+ userleft -= info.size;
+
+ /* get the next record */
+#if DB_VERSION_MAJOR < 2
+ i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
+#else
+ i = 0;
+ if ((errno = dbc->c_get(dbc, &key,
+ &info, DB_NEXT)) != 0)
+ i = 1;
+#endif
+ }
+
+#if DB_VERSION_MAJOR > 1
+ if (dbc != NULL)
+ {
+ (void) dbc->c_close(dbc);
+ dbc = NULL;
+ }
+#endif
+
+ /* if nothing ever matched, try next database */
+ if (!breakout)
+ break;
+
+ message("expanded to %s", user);
+ if (LogLevel >= 10)
+ sm_syslog(LOG_INFO, e->e_id,
+ "expand %.100s => %s",
+ e->e_to,
+ shortenstring(user, MAXSHORTSTR));
+ naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
+ if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
+ {
+ if (tTd(28, 5))
+ {
+ printf("udbexpand: QDONTSEND ");
+ printaddr(a, FALSE);
+ }
+ a->q_flags |= QDONTSEND;
+ }
+ if (i < 0)
+ {
+ syserr("udbexpand: db-get %.*s stat %d",
+ (int) key.size, (char *) key.data, i);
+ return EX_TEMPFAIL;
+ }
+
+ /*
+ ** If this address has a -request address, reflect
+ ** it into the envelope.
+ */
+
+ bzero(&key, sizeof key);
+ bzero(&info, sizeof info);
+ (void) strcpy(keybuf, a->q_user);
+ (void) strcat(keybuf, ":mailsender");
+ keylen = strlen(keybuf);
+ key.data = keybuf;
+ key.size = keylen;
+
+#if DB_VERSION_MAJOR < 2
+ i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
+#else
+ i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
+ &key, &info, 0);
+#endif
+ if (i != 0 || info.size <= 0)
+ break;
+ a->q_owner = xalloc(info.size + 1);
+ bcopy(info.data, a->q_owner, info.size);
+ a->q_owner[info.size] = '\0';
+
+ /* announce delivery; NORECEIPT bit set later */
+ if (e->e_xfp != NULL)
+ {
+ fprintf(e->e_xfp,
+ "Message delivered to mailing list %s\n",
+ a->q_paddr);
+ }
+ e->e_flags |= EF_SENDRECEIPT;
+ a->q_flags |= QDELIVERED|QEXPANDED;
+ break;
+#endif
+
+#ifdef HESIOD
+ case UDB_HESIOD:
+ key.data = keybuf;
+ key.size = keylen;
+ if (tTd(28, 80))
+ printf("udbexpand: trying %s (%d) via hesiod\n",
+ keybuf, keylen);
+ /* look up the key via hesiod */
+ i = hes_udb_get(&key, &info);
+ if (i < 0)
+ {
+ syserr("udbexpand: hesiod-get %.*s stat %d",
+ (int) key.size, (char *) key.data, i);
+ return EX_TEMPFAIL;
+ }
+ else if (i > 0 || info.size <= 0)
+ {
+#if HES_GETMAILHOST
+ struct hes_postoffice *hp;
+#endif
+
+ if (tTd(28, 2))
+ printf("udbexpand: no match on %s (%d)\n",
+ (char *) keybuf, (int) keylen);
+#if HES_GETMAILHOST
+ if (tTd(28, 8))
+ printf(" ... trying hes_getmailhost(%s)\n",
+ a->q_user);
+ hp = hes_getmailhost(a->q_user);
+ if (hp == NULL)
+ {
+ if (hes_error() == HES_ER_NET)
+ {
+ syserr("udbexpand: hesiod-getmail %s stat %d",
+ a->q_user, hes_error());
+ return EX_TEMPFAIL;
+ }
+ if (tTd(28, 2))
+ printf("hes_getmailhost(%s): %d\n",
+ a->q_user, hes_error());
+ break;
+ }
+ if (strlen(hp->po_name) + strlen(hp->po_host) >
+ sizeof pobuf - 2)
+ {
+ if (tTd(28, 2))
+ printf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n",
+ a->q_user,
+ hp->po_name,
+ hp->po_host);
+ break;
+ }
+ info.data = pobuf;
+ snprintf(pobuf, sizeof pobuf, "%s@%s",
+ hp->po_name, hp->po_host);
+ info.size = strlen(info.data);
+#else
+ break;
+#endif
+ }
+ if (tTd(28, 80))
+ printf("udbexpand: match %.*s: %.*s\n",
+ (int) key.size, (char *) key.data,
+ (int) info.size, (char *) info.data);
+ a->q_flags &= ~QSELFREF;
+
+ if (bitset(EF_VRFYONLY, e->e_flags))
+ {
+ a->q_flags |= QVERIFIED;
+ return EX_OK;
+ }
+
+ breakout = TRUE;
+ if (info.size >= usersize)
+ user = xalloc(info.size + 1);
+ bcopy(info.data, user, info.size);
+ user[info.size] = '\0';
+
+ message("hesioded to %s", user);
+ if (LogLevel >= 10)
+ sm_syslog(LOG_INFO, e->e_id,
+ "hesiod %.100s => %s",
+ e->e_to,
+ shortenstring(user, MAXSHORTSTR));
+ naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
+
+ if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
+ {
+ if (tTd(28, 5))
+ {
+ printf("udbexpand: QDONTSEND ");
+ printaddr(a, FALSE);
+ }
+ a->q_flags |= QDONTSEND;
+ }
+
+ /*
+ ** If this address has a -request address, reflect
+ ** it into the envelope.
+ */
+
+ (void) strcpy(keybuf, a->q_user);
+ (void) strcat(keybuf, ":mailsender");
+ keylen = strlen(keybuf);
+ key.data = keybuf;
+ key.size = keylen;
+ i = hes_udb_get(&key, &info);
+ if (i != 0 || info.size <= 0)
+ break;
+ a->q_owner = xalloc(info.size + 1);
+ bcopy(info.data, a->q_owner, info.size);
+ a->q_owner[info.size] = '\0';
+ break;
+#endif /* HESIOD */
+
+ case UDB_REMOTE:
+ /* not yet implemented */
+ break;
+
+ case UDB_FORWARD:
+ if (bitset(EF_VRFYONLY, e->e_flags))
+ return EX_OK;
+ i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
+ if (i >= usersize)
+ {
+ usersize = i + 1;
+ user = xalloc(usersize);
+ }
+ (void) snprintf(user, usersize, "%s@%s",
+ a->q_user, up->udb_fwdhost);
+ message("expanded to %s", user);
+ a->q_flags &= ~QSELFREF;
+ naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
+ if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
+ {
+ if (tTd(28, 5))
+ {
+ printf("udbexpand: QDONTSEND ");
+ printaddr(a, FALSE);
+ }
+ a->q_flags |= QDONTSEND;
+ }
+ breakout = TRUE;
+ break;
+
+ case UDB_EOLIST:
+ breakout = TRUE;
+ break;
+
+ default:
+ /* unknown entry type */
+ break;
+ }
+ if (user != userbuf)
+ free(user);
+ }
+ return EX_OK;
+}
+ /*
+** UDBSENDER -- return canonical external name of sender, given local name
+**
+** Parameters:
+** sender -- the name of the sender on the local machine.
+**
+** Returns:
+** The external name for this sender, if derivable from the
+** database.
+** NULL -- if nothing is changed from the database.
+**
+** Side Effects:
+** none.
+*/
+
+char *
+udbsender(sender)
+ char *sender;
+{
+ extern char *udbmatch __P((char *, char *));
+
+ return udbmatch(sender, "mailname");
+}
+
+
+char *
+udbmatch(user, field)
+ char *user;
+ char *field;
+{
+ register char *p;
+ register struct udbent *up;
+ int i;
+ int keylen;
+ DBT key, info;
+ char keybuf[MAXKEY];
+
+ if (tTd(28, 1))
+ printf("udbmatch(%s, %s)\n", user, field);
+
+ if (!UdbInitialized)
+ {
+ if (_udbx_init(CurEnv) == EX_TEMPFAIL)
+ return NULL;
+ }
+
+ /* short circuit if no spec */
+ if (UdbSpec == NULL || UdbSpec[0] == '\0')
+ return NULL;
+
+ /* short circuit name begins with '\\' since it can't possibly match */
+ if (user[0] == '\\')
+ return NULL;
+
+ /* long names can never match and are a pain to deal with */
+ i = strlen(field);
+ if (i < sizeof "maildrop")
+ i = sizeof "maildrop";
+ if ((strlen(user) + i) > sizeof keybuf - 4)
+ return NULL;
+
+ /* names beginning with colons indicate metadata */
+ if (user[0] == ':')
+ return NULL;
+
+ /* build database key */
+ (void) strcpy(keybuf, user);
+ (void) strcat(keybuf, ":");
+ (void) strcat(keybuf, field);
+ keylen = strlen(keybuf);
+
+ for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
+ {
+ /*
+ ** Select action based on entry type.
+ */
+
+ switch (up->udb_type)
+ {
+#ifdef NEWDB
+ case UDB_DBFETCH:
+ bzero(&key, sizeof key);
+ bzero(&info, sizeof info);
+ key.data = keybuf;
+ key.size = keylen;
+#if DB_VERSION_MAJOR < 2
+ i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
+#else
+ i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
+ &key, &info, 0);
+#endif
+ if (i != 0 || info.size <= 0)
+ {
+ if (tTd(28, 2))
+ printf("udbmatch: no match on %s (%d) via db\n",
+ keybuf, keylen);
+ continue;
+ }
+
+ p = xalloc(info.size + 1);
+ bcopy(info.data, p, info.size);
+ p[info.size] = '\0';
+ if (tTd(28, 1))
+ printf("udbmatch ==> %s\n", p);
+ return p;
+#endif
+
+#ifdef HESIOD
+ case UDB_HESIOD:
+ key.data = keybuf;
+ key.size = keylen;
+ i = hes_udb_get(&key, &info);
+ if (i != 0 || info.size <= 0)
+ {
+ if (tTd(28, 2))
+ printf("udbmatch: no match on %s (%d) via hesiod\n",
+ keybuf, keylen);
+ continue;
+ }
+
+ p = xalloc(info.size + 1);
+ bcopy(info.data, p, info.size);
+ p[info.size] = '\0';
+ if (tTd(28, 1))
+ printf("udbmatch ==> %s\n", p);
+ return p;
+#endif /* HESIOD */
+ }
+ }
+
+ if (strcmp(field, "mailname") != 0)
+ return NULL;
+
+ /*
+ ** Nothing yet. Search again for a default case. But only
+ ** use it if we also have a forward (:maildrop) pointer already
+ ** in the database.
+ */
+
+ /* build database key */
+ (void) strcpy(keybuf, user);
+ (void) strcat(keybuf, ":maildrop");
+ keylen = strlen(keybuf);
+
+ for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
+ {
+ switch (up->udb_type)
+ {
+#ifdef NEWDB
+ case UDB_DBFETCH:
+ /* get the default case for this database */
+ if (up->udb_default == NULL)
+ {
+ bzero(&key, sizeof key);
+ bzero(&info, sizeof info);
+ key.data = ":default:mailname";
+ key.size = strlen(key.data);
+#if DB_VERSION_MAJOR < 2
+ i = (*up->udb_dbp->get)(up->udb_dbp,
+ &key, &info, 0);
+#else
+ i = errno = (*up->udb_dbp->get)(up->udb_dbp,
+ NULL, &key,
+ &info, 0);
+#endif
+ if (i != 0 || info.size <= 0)
+ {
+ /* no default case */
+ up->udb_default = "";
+ continue;
+ }
+
+ /* save the default case */
+ up->udb_default = xalloc(info.size + 1);
+ bcopy(info.data, up->udb_default, info.size);
+ up->udb_default[info.size] = '\0';
+ }
+ else if (up->udb_default[0] == '\0')
+ continue;
+
+ /* we have a default case -- verify user:maildrop */
+ bzero(&key, sizeof key);
+ bzero(&info, sizeof info);
+ key.data = keybuf;
+ key.size = keylen;
+#if DB_VERSION_MAJOR < 2
+ i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
+#else
+ i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL,
+ &key, &info, 0);
+#endif
+ if (i != 0 || info.size <= 0)
+ {
+ /* nope -- no aliasing for this user */
+ continue;
+ }
+
+ /* they exist -- build the actual address */
+ p = xalloc(strlen(user) + strlen(up->udb_default) + 2);
+ (void) strcpy(p, user);
+ (void) strcat(p, "@");
+ (void) strcat(p, up->udb_default);
+ if (tTd(28, 1))
+ printf("udbmatch ==> %s\n", p);
+ return p;
+#endif
+
+#ifdef HESIOD
+ case UDB_HESIOD:
+ /* get the default case for this database */
+ if (up->udb_default == NULL)
+ {
+ key.data = ":default:mailname";
+ key.size = strlen(key.data);
+ i = hes_udb_get(&key, &info);
+
+ if (i != 0 || info.size <= 0)
+ {
+ /* no default case */
+ up->udb_default = "";
+ continue;
+ }
+
+ /* save the default case */
+ up->udb_default = xalloc(info.size + 1);
+ bcopy(info.data, up->udb_default, info.size);
+ up->udb_default[info.size] = '\0';
+ }
+ else if (up->udb_default[0] == '\0')
+ continue;
+
+ /* we have a default case -- verify user:maildrop */
+ key.data = keybuf;
+ key.size = keylen;
+ i = hes_udb_get(&key, &info);
+ if (i != 0 || info.size <= 0)
+ {
+ /* nope -- no aliasing for this user */
+ continue;
+ }
+
+ /* they exist -- build the actual address */
+ p = xalloc(strlen(user) + strlen(up->udb_default) + 2);
+ (void) strcpy(p, user);
+ (void) strcat(p, "@");
+ (void) strcat(p, up->udb_default);
+ if (tTd(28, 1))
+ printf("udbmatch ==> %s\n", p);
+ return p;
+ break;
+#endif /* HESIOD */
+ }
+ }
+
+ /* still nothing.... too bad */
+ return NULL;
+}
+ /*
+** UDB_MAP_LOOKUP -- look up arbitrary entry in user database map
+**
+** Parameters:
+** map -- the map being queried.
+** name -- the name to look up.
+** av -- arguments to the map lookup.
+** statp -- to get any error status.
+**
+** Returns:
+** NULL if name not found in map.
+** The rewritten name otherwise.
+*/
+
+/* ARGSUSED3 */
+char *
+udb_map_lookup(map, name, av, statp)
+ MAP *map;
+ char *name;
+ char **av;
+ int *statp;
+{
+ char *val;
+ char *key;
+ char keybuf[MAXNAME + 1];
+
+ if (tTd(28, 20) || tTd(38, 20))
+ printf("udb_map_lookup(%s, %s)\n", map->map_mname, name);
+
+ if (bitset(MF_NOFOLDCASE, map->map_mflags))
+ {
+ key = name;
+ }
+ else
+ {
+ int keysize = strlen(name);
+
+ if (keysize > sizeof keybuf - 1)
+ keysize = sizeof keybuf - 1;
+ bcopy(name, keybuf, keysize);
+ keybuf[keysize] = '\0';
+ makelower(keybuf);
+ key = keybuf;
+ }
+ val = udbmatch(key, map->map_file);
+ if (val == NULL)
+ return NULL;
+ if (bitset(MF_MATCHONLY, map->map_mflags))
+ return map_rewrite(map, name, strlen(name), NULL);
+ else
+ return map_rewrite(map, val, strlen(val), av);
+}
+ /*
+** _UDBX_INIT -- parse the UDB specification, opening any valid entries.
+**
+** Parameters:
+** e -- the current envelope.
+**
+** Returns:
+** EX_TEMPFAIL -- if it appeared it couldn't get hold of a
+** database due to a host being down or some similar
+** (recoverable) situation.
+** EX_OK -- otherwise.
+**
+** Side Effects:
+** Fills in the UdbEnts structure from UdbSpec.
+*/
+
+#define MAXUDBOPTS 27
+
+int
+_udbx_init(e)
+ ENVELOPE *e;
+{
+ int ents = 0;
+ register char *p;
+ register struct udbent *up;
+
+ if (UdbInitialized)
+ return EX_OK;
+
+# ifdef UDB_DEFAULT_SPEC
+ if (UdbSpec == NULL)
+ UdbSpec = UDB_DEFAULT_SPEC;
+# endif
+
+ p = UdbSpec;
+ up = UdbEnts;
+ while (p != NULL)
+ {
+ char *spec;
+ int l;
+# if 0
+ auto int rcode;
+ int nmx;
+ int i;
+ register struct hostent *h;
+ char *mxhosts[MAXMXHOSTS + 1];
+# endif
+ struct option opts[MAXUDBOPTS + 1];
+ extern int _udb_parsespec __P((char *, struct option [], int));
+
+ while (*p == ' ' || *p == '\t' || *p == ',')
+ p++;
+ if (*p == '\0')
+ break;
+ spec = p;
+ p = strchr(p, ',');
+ if (p != NULL)
+ *p++ = '\0';
+
+ if (ents >= MAXUDBENT)
+ {
+ syserr("Maximum number of UDB entries exceeded");
+ break;
+ }
+
+ /* extract options */
+ (void) _udb_parsespec(spec, opts, MAXUDBOPTS);
+
+ /*
+ ** Decode database specification.
+ **
+ ** In the sendmail tradition, the leading character
+ ** defines the semantics of the rest of the entry.
+ **
+ ** +hostname -- send a datagram to the udb server
+ ** on host "hostname" asking for the
+ ** home mail server for this user.
+ ** *hostname -- similar to +hostname, except that the
+ ** hostname is searched as an MX record;
+ ** resulting hosts are searched as for
+ ** +mxhostname. If no MX host is found,
+ ** this is the same as +hostname.
+ ** @hostname -- forward email to the indicated host.
+ ** This should be the last in the list,
+ ** since it always matches the input.
+ ** /dbname -- search the named database on the local
+ ** host using the Berkeley db package.
+ ** Hesiod -- search the named database with BIND
+ ** using the MIT Hesiod package.
+ */
+
+ switch (*spec)
+ {
+#if 0
+ case '+': /* search remote database */
+ case '*': /* search remote database (expand MX) */
+ if (*spec == '*')
+ {
+#if NAMED_BIND
+ nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode);
+#else
+ mxhosts[0] = spec + 1;
+ nmx = 1;
+ rcode = 0;
+#endif
+ if (tTd(28, 16))
+ {
+ int i;
+
+ printf("getmxrr(%s): %d", spec + 1, nmx);
+ for (i = 0; i <= nmx; i++)
+ printf(" %s", mxhosts[i]);
+ printf("\n");
+ }
+ }
+ else
+ {
+ nmx = 1;
+ mxhosts[0] = spec + 1;
+ }
+
+ for (i = 0; i < nmx; i++)
+ {
+ h = sm_gethostbyname(mxhosts[i]);
+ if (h == NULL)
+ continue;
+ up->udb_type = UDB_REMOTE;
+ up->udb_addr.sin_family = h->h_addrtype;
+ bcopy(h->h_addr_list[0],
+ (char *) &up->udb_addr.sin_addr,
+ INADDRSZ);
+ up->udb_addr.sin_port = UdbPort;
+ up->udb_timeout = UdbTimeout;
+ ents++;
+ up++;
+ }
+
+ /* set up a datagram socket */
+ if (UdbSock < 0)
+ {
+ UdbSock = socket(AF_INET, SOCK_DGRAM, 0);
+ (void) fcntl(UdbSock, F_SETFD, 1);
+ }
+ break;
+#endif
+
+ case '@': /* forward to remote host */
+ up->udb_type = UDB_FORWARD;
+ up->udb_fwdhost = spec + 1;
+ ents++;
+ up++;
+ break;
+
+#ifdef HESIOD
+ case 'h': /* use hesiod */
+ case 'H':
+ if (strcasecmp(spec, "hesiod") != 0)
+ goto badspec;
+ up->udb_type = UDB_HESIOD;
+ ents++;
+ up++;
+ break;
+#endif /* HESIOD */
+
+#ifdef NEWDB
+ case '/': /* look up remote name */
+ l = strlen(spec);
+ if (l > 3 && strcmp(&spec[l - 3], ".db") == 0)
+ {
+ up->udb_dbname = spec;
+ }
+ else
+ {
+ up->udb_dbname = xalloc(l + 4);
+ strcpy(up->udb_dbname, spec);
+ strcat(up->udb_dbname, ".db");
+ }
+ errno = 0;
+#if DB_VERSION_MAJOR < 2
+ up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY,
+ 0644, DB_BTREE, NULL);
+#else
+ up->udb_dbp = NULL;
+ errno = db_open(up->udb_dbname, DB_BTREE, DB_RDONLY,
+ 0644, NULL, NULL, &up->udb_dbp);
+#endif
+ if (up->udb_dbp == NULL)
+ {
+ if (tTd(28, 1))
+ {
+ int saveerrno = errno;
+
+#if DB_VERSION_MAJOR < 2
+ printf("dbopen(%s): %s\n",
+#else
+ printf("db_open(%s): %s\n",
+#endif
+ up->udb_dbname,
+ errstring(errno));
+ errno = saveerrno;
+ }
+ if (errno != ENOENT && errno != EACCES)
+ {
+ if (LogLevel > 2)
+ sm_syslog(LOG_ERR, e->e_id,
+#if DB_VERSION_MAJOR < 2
+ "dbopen(%s): %s",
+#else
+ "db_open(%s): %s",
+#endif
+ up->udb_dbname,
+ errstring(errno));
+ up->udb_type = UDB_EOLIST;
+ if (up->udb_dbname != spec)
+ free(up->udb_dbname);
+ goto tempfail;
+ }
+ if (up->udb_dbname != spec)
+ free(up->udb_dbname);
+ break;
+ }
+ up->udb_type = UDB_DBFETCH;
+ ents++;
+ up++;
+ break;
+#endif
+
+ default:
+badspec:
+ syserr("Unknown UDB spec %s", spec);
+ break;
+ }
+ }
+ up->udb_type = UDB_EOLIST;
+
+ if (tTd(28, 4))
+ {
+ for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
+ {
+ switch (up->udb_type)
+ {
+#if DAEMON
+ case UDB_REMOTE:
+ printf("REMOTE: addr %s, timeo %d\n",
+ anynet_ntoa((SOCKADDR *) &up->udb_addr),
+ up->udb_timeout);
+ break;
+#endif
+
+ case UDB_DBFETCH:
+#ifdef NEWDB
+ printf("FETCH: file %s\n",
+ up->udb_dbname);
+#else
+ printf("FETCH\n");
+#endif
+ break;
+
+ case UDB_FORWARD:
+ printf("FORWARD: host %s\n",
+ up->udb_fwdhost);
+ break;
+
+ case UDB_HESIOD:
+ printf("HESIOD\n");
+ break;
+
+ default:
+ printf("UNKNOWN\n");
+ break;
+ }
+ }
+ }
+
+ UdbInitialized = TRUE;
+ errno = 0;
+ return EX_OK;
+
+ /*
+ ** On temporary failure, back out anything we've already done
+ */
+
+ tempfail:
+#ifdef NEWDB
+ for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
+ {
+ if (up->udb_type == UDB_DBFETCH)
+ {
+#if DB_VERSION_MAJOR < 2
+ (*up->udb_dbp->close)(up->udb_dbp);
+#else
+ errno = (*up->udb_dbp->close)(up->udb_dbp, 0);
+#endif
+ }
+ }
+#endif
+ return EX_TEMPFAIL;
+}
+
+int
+_udb_parsespec(udbspec, opt, maxopts)
+ char *udbspec;
+ struct option opt[];
+ int maxopts;
+{
+ register char *spec;
+ register char *spec_end;
+ register int optnum;
+
+ spec_end = strchr(udbspec, ':');
+ for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
+ {
+ register char *p;
+
+ while (isascii(*spec) && isspace(*spec))
+ spec++;
+ spec_end = strchr(spec, ':');
+ if (spec_end != NULL)
+ *spec_end++ = '\0';
+
+ opt[optnum].name = spec;
+ opt[optnum].val = NULL;
+ p = strchr(spec, '=');
+ if (p != NULL)
+ opt[optnum].val = ++p;
+ }
+ return optnum;
+}
+
+#ifdef HESIOD
+
+int
+hes_udb_get(key, info)
+ DBT *key;
+ DBT *info;
+{
+ char *name, *type;
+ char **hp;
+ char kbuf[MAXKEY + 1];
+
+ if (strlen(key->data) >= (SIZE_T) sizeof kbuf)
+ return 0;
+ strcpy(kbuf, key->data);
+ name = kbuf;
+ type = strrchr(name, ':');
+ if (type == NULL)
+ return 1;
+ *type++ = '\0';
+ if (strchr(name, '@') != NULL)
+ return 1;
+
+ if (tTd(28, 1))
+ printf("hes_udb_get(%s, %s)\n", name, type);
+
+ /* make the hesiod query */
+#ifdef HESIOD_INIT
+ if (HesiodContext == NULL && hesiod_init(&HesiodContext) != 0)
+ return -1;
+ hp = hesiod_resolve(HesiodContext, name, type);
+#else
+ hp = hes_resolve(name, type);
+#endif /* HESIOD_INIT */
+ *--type = ':';
+#ifdef HESIOD_INIT
+ if (hp == NULL)
+ return 1;
+ if (*hp == NULL)
+ {
+ hesiod_free_list(HesiodContext, hp);
+ if (errno == ECONNREFUSED || errno == EMSGSIZE)
+ return -1;
+ return 1;
+ }
+#else
+ if (hp == NULL || hp[0] == NULL)
+ {
+ /* network problem or timeout */
+ if (hes_error() == HES_ER_NET)
+ return -1;
+
+ return 1;
+ }
+#endif /* HESIOD_INIT */
+ else
+ {
+ /*
+ ** If there are multiple matches, just return the
+ ** first one.
+ **
+ ** XXX These should really be returned; for example,
+ ** XXX it is legal for :maildrop to be multi-valued.
+ */
+
+ info->data = hp[0];
+ info->size = (size_t) strlen(info->data);
+ }
+
+ if (tTd(28, 80))
+ printf("hes_udb_get => %s\n", *hp);
+
+ return 0;
+}
+#endif /* HESIOD */
+
+#else /* not USERDB */
+
+int
+udbexpand(a, sendq, aliaslevel, e)
+ ADDRESS *a;
+ ADDRESS **sendq;
+ int aliaslevel;
+ ENVELOPE *e;
+{
+ return EX_OK;
+}
+
+#endif /* USERDB */
diff --git a/contrib/sendmail/src/useful.h b/contrib/sendmail/src/useful.h
new file mode 100644
index 0000000..2a283ab
--- /dev/null
+++ b/contrib/sendmail/src/useful.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ *
+ * @(#)useful.h 8.12 (Berkeley) 5/19/98
+ */
+
+# include <sys/types.h>
+
+/* support for bool type */
+typedef int bool;
+#ifndef TRUE
+# define TRUE 1
+# define FALSE 0
+#endif
+
+# ifndef NULL
+# define NULL 0
+# endif /* NULL */
+
+/* bit hacking */
+# define bitset(bit, word) (((word) & (bit)) != 0)
+
+/* some simple functions */
+# ifndef max
+# define max(a, b) ((a) > (b) ? (a) : (b))
+# define min(a, b) ((a) < (b) ? (a) : (b))
+# endif
+
+/* assertions */
+# ifndef NASSERT
+# define ASSERT(expr, msg, parm)\
+ if (!(expr))\
+ {\
+ fprintf(stderr, "assertion botch: %s:%d: ", __FILE__, __LINE__);\
+ fprintf(stderr, msg, parm);\
+ }
+# else /* NASSERT */
+# define ASSERT(expr, msg, parm)
+# endif /* NASSERT */
+
+/* sccs id's */
+# ifndef lint
+# ifdef __STDC__
+# define SCCSID(arg) static char SccsId[] = #arg;
+# else
+# define SCCSID(arg) static char SccsId[] = "arg";
+# endif
+# else
+# define SCCSID(arg)
+# endif
diff --git a/contrib/sendmail/src/usersmtp.c b/contrib/sendmail/src/usersmtp.c
new file mode 100644
index 0000000..23e4d02
--- /dev/null
+++ b/contrib/sendmail/src/usersmtp.c
@@ -0,0 +1,1166 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+# include "sendmail.h"
+
+#ifndef lint
+#if SMTP
+static char sccsid[] = "@(#)usersmtp.c 8.104 (Berkeley) 6/30/98 (with SMTP)";
+#else
+static char sccsid[] = "@(#)usersmtp.c 8.104 (Berkeley) 6/30/98 (without SMTP)";
+#endif
+#endif /* not lint */
+
+# include <sysexits.h>
+# include <errno.h>
+
+# if SMTP
+
+/*
+** USERSMTP -- run SMTP protocol from the user end.
+**
+** This protocol is described in RFC821.
+*/
+
+#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
+#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
+#define SMTPCLOSING 421 /* "Service Shutting Down" */
+
+char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
+char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
+char SmtpError[MAXLINE] = ""; /* save failure error messages */
+bool SmtpNeedIntro; /* need "while talking" in transcript */
+
+extern void smtpmessage __P((char *f, MAILER *m, MCI *mci, ...));
+extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)()));
+ /*
+** SMTPINIT -- initialize SMTP.
+**
+** Opens the connection and sends the initial protocol.
+**
+** Parameters:
+** m -- mailer to create connection to.
+** pvp -- pointer to parameter vector to pass to
+** the mailer.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** creates connection and sends initial protocol.
+*/
+
+void
+smtpinit(m, mci, e)
+ MAILER *m;
+ register MCI *mci;
+ ENVELOPE *e;
+{
+ register int r;
+ register char *p;
+ extern void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
+ extern void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
+
+ if (tTd(18, 1))
+ {
+ printf("smtpinit ");
+ mci_dump(mci, FALSE);
+ }
+
+ /*
+ ** Open the connection to the mailer.
+ */
+
+ SmtpError[0] = '\0';
+ CurHostName = mci->mci_host; /* XXX UGLY XXX */
+ if (CurHostName == NULL)
+ CurHostName = MyHostName;
+ SmtpNeedIntro = TRUE;
+ switch (mci->mci_state)
+ {
+ case MCIS_ACTIVE:
+ /* need to clear old information */
+ smtprset(m, mci, e);
+ /* fall through */
+
+ case MCIS_OPEN:
+ return;
+
+ case MCIS_ERROR:
+ case MCIS_SSD:
+ /* shouldn't happen */
+ smtpquit(m, mci, e);
+ /* fall through */
+
+ case MCIS_CLOSED:
+ syserr("451 smtpinit: state CLOSED");
+ return;
+
+ case MCIS_OPENING:
+ break;
+ }
+
+ mci->mci_state = MCIS_OPENING;
+
+ /*
+ ** Get the greeting message.
+ ** This should appear spontaneously. Give it five minutes to
+ ** happen.
+ */
+
+ SmtpPhase = mci->mci_phase = "client greeting";
+ setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+ r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
+ if (r < 0)
+ goto tempfail1;
+ if (REPLYTYPE(r) == 4)
+ goto tempfail2;
+ if (REPLYTYPE(r) != 2)
+ goto unavailable;
+
+ /*
+ ** Send the HELO command.
+ ** My mother taught me to always introduce myself.
+ */
+
+ if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
+ mci->mci_flags |= MCIF_ESMTP;
+
+tryhelo:
+ if (bitnset(M_LMTP, m->m_flags))
+ {
+ smtpmessage("LHLO %s", m, mci, MyHostName);
+ SmtpPhase = mci->mci_phase = "client LHLO";
+ }
+ else if (bitset(MCIF_ESMTP, mci->mci_flags))
+ {
+ smtpmessage("EHLO %s", m, mci, MyHostName);
+ SmtpPhase = mci->mci_phase = "client EHLO";
+ }
+ else
+ {
+ smtpmessage("HELO %s", m, mci, MyHostName);
+ SmtpPhase = mci->mci_phase = "client HELO";
+ }
+ setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+ r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
+ if (r < 0)
+ goto tempfail1;
+ else if (REPLYTYPE(r) == 5)
+ {
+ if (bitset(MCIF_ESMTP, mci->mci_flags) &&
+ !bitnset(M_LMTP, m->m_flags))
+ {
+ /* try old SMTP instead */
+ mci->mci_flags &= ~MCIF_ESMTP;
+ goto tryhelo;
+ }
+ goto unavailable;
+ }
+ else if (REPLYTYPE(r) != 2)
+ goto tempfail2;
+
+ /*
+ ** Check to see if we actually ended up talking to ourself.
+ ** This means we didn't know about an alias or MX, or we managed
+ ** to connect to an echo server.
+ */
+
+ p = strchr(&SmtpReplyBuffer[4], ' ');
+ if (p != NULL)
+ *p = '\0';
+ if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
+ !bitnset(M_LMTP, m->m_flags) &&
+ strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
+ {
+ syserr("553 %s config error: mail loops back to me (MX problem?)",
+ CurHostName);
+ mci_setstat(mci, EX_CONFIG, NULL, NULL);
+ mci->mci_errno = 0;
+ smtpquit(m, mci, e);
+ return;
+ }
+
+ /*
+ ** If this is expected to be another sendmail, send some internal
+ ** commands.
+ */
+
+ if (bitnset(M_INTERNAL, m->m_flags))
+ {
+ /* tell it to be verbose */
+ smtpmessage("VERB", m, mci);
+ r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
+ if (r < 0)
+ goto tempfail1;
+ }
+
+ if (mci->mci_state != MCIS_CLOSED)
+ {
+ mci->mci_state = MCIS_OPEN;
+ return;
+ }
+
+ /* got a 421 error code during startup */
+
+ tempfail1:
+ if (mci->mci_errno == 0)
+ mci->mci_errno = errno;
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
+ if (mci->mci_state != MCIS_CLOSED)
+ smtpquit(m, mci, e);
+ return;
+
+ tempfail2:
+ if (mci->mci_errno == 0)
+ mci->mci_errno = errno;
+ /* XXX should use code from other end iff ENHANCEDSTATUSCODES */
+ mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer);
+ if (mci->mci_state != MCIS_CLOSED)
+ smtpquit(m, mci, e);
+ return;
+
+ unavailable:
+ mci->mci_errno = errno;
+ mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
+ smtpquit(m, mci, e);
+ return;
+}
+ /*
+** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
+**
+** Parameters:
+** line -- the response line.
+** firstline -- set if this is the first line of the reply.
+** m -- the mailer.
+** mci -- the mailer connection info.
+** e -- the envelope.
+**
+** Returns:
+** none.
+*/
+
+void
+esmtp_check(line, firstline, m, mci, e)
+ char *line;
+ bool firstline;
+ MAILER *m;
+ register MCI *mci;
+ ENVELOPE *e;
+{
+ if (strstr(line, "ESMTP") != NULL)
+ mci->mci_flags |= MCIF_ESMTP;
+ if (strstr(line, "8BIT-OK") != NULL)
+ mci->mci_flags |= MCIF_8BITOK;
+}
+ /*
+** HELO_OPTIONS -- process the options on a HELO line.
+**
+** Parameters:
+** line -- the response line.
+** firstline -- set if this is the first line of the reply.
+** m -- the mailer.
+** mci -- the mailer connection info.
+** e -- the envelope.
+**
+** Returns:
+** none.
+*/
+
+void
+helo_options(line, firstline, m, mci, e)
+ char *line;
+ bool firstline;
+ MAILER *m;
+ register MCI *mci;
+ ENVELOPE *e;
+{
+ register char *p;
+
+ if (firstline)
+ return;
+
+ if (strlen(line) < (SIZE_T) 5)
+ return;
+ line += 4;
+ p = strchr(line, ' ');
+ if (p != NULL)
+ *p++ = '\0';
+ if (strcasecmp(line, "size") == 0)
+ {
+ mci->mci_flags |= MCIF_SIZE;
+ if (p != NULL)
+ mci->mci_maxsize = atol(p);
+ }
+ else if (strcasecmp(line, "8bitmime") == 0)
+ {
+ mci->mci_flags |= MCIF_8BITMIME;
+ mci->mci_flags &= ~MCIF_7BIT;
+ }
+ else if (strcasecmp(line, "expn") == 0)
+ mci->mci_flags |= MCIF_EXPN;
+ else if (strcasecmp(line, "dsn") == 0)
+ mci->mci_flags |= MCIF_DSN;
+}
+ /*
+** SMTPMAILFROM -- send MAIL command
+**
+** Parameters:
+** m -- the mailer.
+** mci -- the mailer connection structure.
+** e -- the envelope (including the sender to specify).
+*/
+
+int
+smtpmailfrom(m, mci, e)
+ MAILER *m;
+ MCI *mci;
+ ENVELOPE *e;
+{
+ int r;
+ int l;
+ char *bufp;
+ char *bodytype;
+ char buf[MAXNAME + 1];
+ char optbuf[MAXLINE];
+
+ if (tTd(18, 2))
+ printf("smtpmailfrom: CurHost=%s\n", CurHostName);
+
+ /* set up appropriate options to include */
+ if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
+ snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
+ else
+ strcpy(optbuf, "");
+ l = sizeof optbuf - strlen(optbuf) - 1;
+
+ bodytype = e->e_bodytype;
+ if (bitset(MCIF_8BITMIME, mci->mci_flags))
+ {
+ if (bodytype == NULL &&
+ bitset(MM_MIME8BIT, MimeMode) &&
+ bitset(EF_HAS8BIT, e->e_flags) &&
+ !bitset(EF_DONT_MIME, e->e_flags) &&
+ !bitnset(M_8BITS, m->m_flags))
+ bodytype = "8BITMIME";
+ if (bodytype != NULL && strlen(bodytype) + 7 < l)
+ {
+ strcat(optbuf, " BODY=");
+ strcat(optbuf, bodytype);
+ l -= strlen(optbuf);
+ }
+ }
+ else if (bitnset(M_8BITS, m->m_flags) ||
+ !bitset(EF_HAS8BIT, e->e_flags) ||
+ bitset(MCIF_8BITOK, mci->mci_flags))
+ {
+ /* just pass it through */
+ }
+#if MIME8TO7
+ else if (bitset(MM_CVTMIME, MimeMode) &&
+ !bitset(EF_DONT_MIME, e->e_flags) &&
+ (!bitset(MM_PASS8BIT, MimeMode) ||
+ bitset(EF_IS_MIME, e->e_flags)))
+ {
+ /* must convert from 8bit MIME format to 7bit encoded */
+ mci->mci_flags |= MCIF_CVT8TO7;
+ }
+#endif
+ else if (!bitset(MM_PASS8BIT, MimeMode))
+ {
+ /* cannot just send a 8-bit version */
+ extern char MsgBuf[];
+
+ usrerr("%s does not support 8BITMIME", CurHostName);
+ mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
+ return EX_DATAERR;
+ }
+
+ if (bitset(MCIF_DSN, mci->mci_flags))
+ {
+ if (e->e_envid != NULL && strlen(e->e_envid) < (SIZE_T) (l - 7))
+ {
+ strcat(optbuf, " ENVID=");
+ strcat(optbuf, e->e_envid);
+ l -= strlen(optbuf);
+ }
+
+ /* RET= parameter */
+ if (bitset(EF_RET_PARAM, e->e_flags) && l >= 9)
+ {
+ strcat(optbuf, " RET=");
+ if (bitset(EF_NO_BODY_RETN, e->e_flags))
+ strcat(optbuf, "HDRS");
+ else
+ strcat(optbuf, "FULL");
+ l -= 9;
+ }
+ }
+
+ /*
+ ** Send the MAIL command.
+ ** Designates the sender.
+ */
+
+ mci->mci_state = MCIS_ACTIVE;
+
+ if (bitset(EF_RESPONSE, e->e_flags) &&
+ !bitnset(M_NO_NULL_FROM, m->m_flags))
+ (void) strcpy(buf, "");
+ else
+ expand("\201g", buf, sizeof buf, e);
+ if (buf[0] == '<')
+ {
+ /* strip off <angle brackets> (put back on below) */
+ bufp = &buf[strlen(buf) - 1];
+ if (*bufp == '>')
+ *bufp = '\0';
+ bufp = &buf[1];
+ }
+ else
+ bufp = buf;
+ if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
+ !bitnset(M_FROMPATH, m->m_flags))
+ {
+ smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
+ }
+ else
+ {
+ smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
+ *bufp == '@' ? ',' : ':', bufp, optbuf);
+ }
+ SmtpPhase = mci->mci_phase = "client MAIL";
+ setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+ r = reply(m, mci, e, TimeOuts.to_mail, NULL);
+ if (r < 0)
+ {
+ /* communications failure */
+ mci->mci_errno = errno;
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
+ smtpquit(m, mci, e);
+ return EX_TEMPFAIL;
+ }
+ else if (r == 421)
+ {
+ /* service shutting down */
+ mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer);
+ smtpquit(m, mci, e);
+ return EX_TEMPFAIL;
+ }
+ else if (REPLYTYPE(r) == 4)
+ {
+ mci_setstat(mci, EX_NOTSTICKY, smtptodsn(r), SmtpReplyBuffer);
+ return EX_TEMPFAIL;
+ }
+ else if (REPLYTYPE(r) == 2)
+ {
+ return EX_OK;
+ }
+ else if (r == 501)
+ {
+ /* syntax error in arguments */
+ mci_setstat(mci, EX_NOTSTICKY, "5.5.2", SmtpReplyBuffer);
+ return EX_DATAERR;
+ }
+ else if (r == 553)
+ {
+ /* mailbox name not allowed */
+ mci_setstat(mci, EX_NOTSTICKY, "5.1.3", SmtpReplyBuffer);
+ return EX_DATAERR;
+ }
+ else if (r == 552)
+ {
+ /* exceeded storage allocation */
+ mci_setstat(mci, EX_NOTSTICKY, "5.3.4", SmtpReplyBuffer);
+ if (bitset(MCIF_SIZE, mci->mci_flags))
+ e->e_flags |= EF_NO_BODY_RETN;
+ return EX_UNAVAILABLE;
+ }
+ else if (REPLYTYPE(r) == 5)
+ {
+ /* unknown error */
+ mci_setstat(mci, EX_NOTSTICKY, "5.0.0", SmtpReplyBuffer);
+ return EX_UNAVAILABLE;
+ }
+
+ if (LogLevel > 1)
+ {
+ sm_syslog(LOG_CRIT, e->e_id,
+ "%.100s: SMTP MAIL protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
+ }
+
+ /* protocol error -- close up */
+ mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer);
+ smtpquit(m, mci, e);
+ return EX_PROTOCOL;
+}
+ /*
+** SMTPRCPT -- designate recipient.
+**
+** Parameters:
+** to -- address of recipient.
+** m -- the mailer we are sending to.
+** mci -- the connection info for this transaction.
+** e -- the envelope for this transaction.
+**
+** Returns:
+** exit status corresponding to recipient status.
+**
+** Side Effects:
+** Sends the mail via SMTP.
+*/
+
+int
+smtprcpt(to, m, mci, e)
+ ADDRESS *to;
+ register MAILER *m;
+ MCI *mci;
+ ENVELOPE *e;
+{
+ register int r;
+ int l;
+ char optbuf[MAXLINE];
+
+ strcpy(optbuf, "");
+ l = sizeof optbuf - 1;
+ if (bitset(MCIF_DSN, mci->mci_flags))
+ {
+ /* NOTIFY= parameter */
+ if (bitset(QHASNOTIFY, to->q_flags) &&
+ bitset(QPRIMARY, to->q_flags) &&
+ !bitnset(M_LOCALMAILER, m->m_flags))
+ {
+ bool firstone = TRUE;
+
+ strcat(optbuf, " NOTIFY=");
+ if (bitset(QPINGONSUCCESS, to->q_flags))
+ {
+ strcat(optbuf, "SUCCESS");
+ firstone = FALSE;
+ }
+ if (bitset(QPINGONFAILURE, to->q_flags))
+ {
+ if (!firstone)
+ strcat(optbuf, ",");
+ strcat(optbuf, "FAILURE");
+ firstone = FALSE;
+ }
+ if (bitset(QPINGONDELAY, to->q_flags))
+ {
+ if (!firstone)
+ strcat(optbuf, ",");
+ strcat(optbuf, "DELAY");
+ firstone = FALSE;
+ }
+ if (firstone)
+ strcat(optbuf, "NEVER");
+ l -= strlen(optbuf);
+ }
+
+ /* ORCPT= parameter */
+ if (to->q_orcpt != NULL && strlen(to->q_orcpt) + 7 < l)
+ {
+ strcat(optbuf, " ORCPT=");
+ strcat(optbuf, to->q_orcpt);
+ l -= strlen(optbuf);
+ }
+ }
+
+ smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
+
+ SmtpPhase = mci->mci_phase = "client RCPT";
+ setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+ r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
+ to->q_rstatus = newstr(SmtpReplyBuffer);
+ to->q_status = smtptodsn(r);
+ to->q_statmta = mci->mci_host;
+ if (r < 0 || REPLYTYPE(r) == 4)
+ return EX_TEMPFAIL;
+ else if (REPLYTYPE(r) == 2)
+ return EX_OK;
+ else if (r == 550)
+ {
+ to->q_status = "5.1.1";
+ return EX_NOUSER;
+ }
+ else if (r == 551)
+ {
+ to->q_status = "5.1.6";
+ return EX_NOUSER;
+ }
+ else if (r == 553)
+ {
+ to->q_status = "5.1.3";
+ return EX_NOUSER;
+ }
+ else if (REPLYTYPE(r) == 5)
+ {
+ return EX_UNAVAILABLE;
+ }
+
+ if (LogLevel > 1)
+ {
+ sm_syslog(LOG_CRIT, e->e_id,
+ "%.100s: SMTP RCPT protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
+ }
+
+ mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer);
+ return EX_PROTOCOL;
+}
+ /*
+** SMTPDATA -- send the data and clean up the transaction.
+**
+** Parameters:
+** m -- mailer being sent to.
+** mci -- the mailer connection information.
+** e -- the envelope for this message.
+**
+** Returns:
+** exit status corresponding to DATA command.
+**
+** Side Effects:
+** none.
+*/
+
+static jmp_buf CtxDataTimeout;
+static void datatimeout __P((void));
+
+int
+smtpdata(m, mci, e)
+ MAILER *m;
+ register MCI *mci;
+ register ENVELOPE *e;
+{
+ register int r;
+ register EVENT *ev;
+ int rstat;
+ int xstat;
+ time_t timeout;
+
+ /*
+ ** Send the data.
+ ** First send the command and check that it is ok.
+ ** Then send the data.
+ ** Follow it up with a dot to terminate.
+ ** Finally get the results of the transaction.
+ */
+
+ /* send the command and check ok to proceed */
+ smtpmessage("DATA", m, mci);
+ SmtpPhase = mci->mci_phase = "client DATA 354";
+ setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+ r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
+ if (r < 0 || REPLYTYPE(r) == 4)
+ {
+ smtpquit(m, mci, e);
+ return EX_TEMPFAIL;
+ }
+ else if (REPLYTYPE(r) == 5)
+ {
+ smtprset(m, mci, e);
+ return EX_UNAVAILABLE;
+ }
+ else if (r != 354)
+ {
+ if (LogLevel > 1)
+ {
+ sm_syslog(LOG_CRIT, e->e_id,
+ "%.100s: SMTP DATA-1 protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
+ }
+ smtprset(m, mci, e);
+ mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer);
+ return (EX_PROTOCOL);
+ }
+
+ /*
+ ** Set timeout around data writes. Make it at least large
+ ** enough for DNS timeouts on all recipients plus some fudge
+ ** factor. The main thing is that it should not be infinite.
+ */
+
+ if (setjmp(CtxDataTimeout) != 0)
+ {
+ mci->mci_errno = errno;
+ mci->mci_state = MCIS_ERROR;
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
+ syserr("451 timeout writing message to %s", CurHostName);
+ smtpquit(m, mci, e);
+ return EX_TEMPFAIL;
+ }
+
+ timeout = e->e_msgsize / 16;
+ if (timeout < (time_t) 600)
+ timeout = (time_t) 600;
+ timeout += e->e_nrcpts * 300;
+ ev = setevent(timeout, datatimeout, 0);
+
+ /*
+ ** Output the actual message.
+ */
+
+ (*e->e_puthdr)(mci, e->e_header, e);
+ (*e->e_putbody)(mci, e, NULL);
+
+ /*
+ ** Cleanup after sending message.
+ */
+
+ clrevent(ev);
+
+ if (ferror(mci->mci_out))
+ {
+ /* error during processing -- don't send the dot */
+ mci->mci_errno = EIO;
+ mci->mci_state = MCIS_ERROR;
+ mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
+ smtpquit(m, mci, e);
+ return EX_IOERR;
+ }
+
+ /* terminate the message */
+ fprintf(mci->mci_out, ".%s", m->m_eol);
+ if (TrafficLogFile != NULL)
+ fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid());
+ if (Verbose)
+ nmessage(">>> .");
+
+ /* check for the results of the transaction */
+ SmtpPhase = mci->mci_phase = "client DATA status";
+ setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
+ if (bitnset(M_LMTP, m->m_flags))
+ return EX_OK;
+ r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
+ if (r < 0)
+ {
+ smtpquit(m, mci, e);
+ return EX_TEMPFAIL;
+ }
+ mci->mci_state = MCIS_OPEN;
+ xstat = EX_NOTSTICKY;
+ if (r == 452)
+ rstat = EX_TEMPFAIL;
+ else if (REPLYTYPE(r) == 4)
+ rstat = xstat = EX_TEMPFAIL;
+ else if (REPLYCLASS(r) != 5)
+ rstat = xstat = EX_PROTOCOL;
+ else if (REPLYTYPE(r) == 2)
+ rstat = xstat = EX_OK;
+ else if (REPLYTYPE(r) == 5)
+ rstat = EX_UNAVAILABLE;
+ else
+ rstat = EX_PROTOCOL;
+ mci_setstat(mci, xstat, smtptodsn(r), SmtpReplyBuffer);
+ if (e->e_statmsg != NULL)
+ free(e->e_statmsg);
+ e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
+ if (rstat != EX_PROTOCOL)
+ return rstat;
+ if (LogLevel > 1)
+ {
+ sm_syslog(LOG_CRIT, e->e_id,
+ "%.100s: SMTP DATA-2 protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
+ }
+ return rstat;
+}
+
+
+static void
+datatimeout()
+{
+ longjmp(CtxDataTimeout, 1);
+}
+ /*
+** SMTPGETSTAT -- get status code from DATA in LMTP
+**
+** Parameters:
+** m -- the mailer to which we are sending the message.
+** mci -- the mailer connection structure.
+** e -- the current envelope.
+**
+** Returns:
+** The exit status corresponding to the reply code.
+*/
+
+int
+smtpgetstat(m, mci, e)
+ MAILER *m;
+ MCI *mci;
+ ENVELOPE *e;
+{
+ int r;
+ int stat;
+
+ /* check for the results of the transaction */
+ r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
+ if (r < 0)
+ {
+ smtpquit(m, mci, e);
+ return EX_TEMPFAIL;
+ }
+ if (e->e_statmsg != NULL)
+ free(e->e_statmsg);
+ e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
+ if (REPLYTYPE(r) == 4)
+ stat = EX_TEMPFAIL;
+ else if (REPLYCLASS(r) != 5)
+ stat = EX_PROTOCOL;
+ else if (REPLYTYPE(r) == 2)
+ stat = EX_OK;
+ else if (REPLYTYPE(r) == 5)
+ stat = EX_UNAVAILABLE;
+ else
+ stat = EX_PROTOCOL;
+ mci_setstat(mci, stat, smtptodsn(r), SmtpReplyBuffer);
+ if (LogLevel > 1 && stat == EX_PROTOCOL)
+ {
+ sm_syslog(LOG_CRIT, e->e_id,
+ "%.100s: SMTP DATA-3 protocol error: %s",
+ CurHostName,
+ shortenstring(SmtpReplyBuffer, 403));
+ }
+ return stat;
+}
+ /*
+** SMTPQUIT -- close the SMTP connection.
+**
+** Parameters:
+** m -- a pointer to the mailer.
+** mci -- the mailer connection information.
+** e -- the current envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** sends the final protocol and closes the connection.
+*/
+
+void
+smtpquit(m, mci, e)
+ register MAILER *m;
+ register MCI *mci;
+ ENVELOPE *e;
+{
+ bool oldSuprErrs = SuprErrs;
+
+ /*
+ ** Suppress errors here -- we may be processing a different
+ ** job when we do the quit connection, and we don't want the
+ ** new job to be penalized for something that isn't it's
+ ** problem.
+ */
+
+ SuprErrs = TRUE;
+
+ /* send the quit message if we haven't gotten I/O error */
+ if (mci->mci_state != MCIS_ERROR)
+ {
+ SmtpPhase = "client QUIT";
+ smtpmessage("QUIT", m, mci);
+ (void) reply(m, mci, e, TimeOuts.to_quit, NULL);
+ SuprErrs = oldSuprErrs;
+ if (mci->mci_state == MCIS_CLOSED)
+ return;
+ }
+
+ /* now actually close the connection and pick up the zombie */
+ (void) endmailer(mci, e, NULL);
+
+ SuprErrs = oldSuprErrs;
+}
+ /*
+** SMTPRSET -- send a RSET (reset) command
+*/
+
+void
+smtprset(m, mci, e)
+ register MAILER *m;
+ register MCI *mci;
+ ENVELOPE *e;
+{
+ int r;
+
+ SmtpPhase = "client RSET";
+ smtpmessage("RSET", m, mci);
+ r = reply(m, mci, e, TimeOuts.to_rset, NULL);
+ if (r < 0)
+ mci->mci_state = MCIS_ERROR;
+ else if (REPLYTYPE(r) == 2)
+ {
+ mci->mci_state = MCIS_OPEN;
+ return;
+ }
+ smtpquit(m, mci, e);
+}
+ /*
+** SMTPPROBE -- check the connection state
+*/
+
+int
+smtpprobe(mci)
+ register MCI *mci;
+{
+ int r;
+ MAILER *m = mci->mci_mailer;
+ extern ENVELOPE BlankEnvelope;
+ ENVELOPE *e = &BlankEnvelope;
+
+ SmtpPhase = "client probe";
+ smtpmessage("RSET", m, mci);
+ r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
+ if (r < 0 || REPLYTYPE(r) != 2)
+ smtpquit(m, mci, e);
+ return r;
+}
+ /*
+** REPLY -- read arpanet reply
+**
+** Parameters:
+** m -- the mailer we are reading the reply from.
+** mci -- the mailer connection info structure.
+** e -- the current envelope.
+** timeout -- the timeout for reads.
+** pfunc -- processing function called on each line of response.
+** If null, no special processing is done.
+**
+** Returns:
+** reply code it reads.
+**
+** Side Effects:
+** flushes the mail file.
+*/
+
+int
+reply(m, mci, e, timeout, pfunc)
+ MAILER *m;
+ MCI *mci;
+ ENVELOPE *e;
+ time_t timeout;
+ void (*pfunc)();
+{
+ register char *bufp;
+ register int r;
+ bool firstline = TRUE;
+ char junkbuf[MAXLINE];
+
+ if (mci->mci_out != NULL)
+ (void) fflush(mci->mci_out);
+
+ if (tTd(18, 1))
+ printf("reply\n");
+
+ /*
+ ** Read the input line, being careful not to hang.
+ */
+
+ bufp = SmtpReplyBuffer;
+ for (;;)
+ {
+ register char *p;
+ extern time_t curtime __P((void));
+
+ /* actually do the read */
+ if (e->e_xfp != NULL)
+ (void) fflush(e->e_xfp); /* for debugging */
+
+ /* if we are in the process of closing just give the code */
+ if (mci->mci_state == MCIS_CLOSED)
+ return (SMTPCLOSING);
+
+ if (mci->mci_out != NULL)
+ fflush(mci->mci_out);
+
+ /* get the line from the other side */
+ p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
+ mci->mci_lastuse = curtime();
+
+ if (p == NULL)
+ {
+ bool oldholderrs;
+ extern char MsgBuf[];
+
+ /* if the remote end closed early, fake an error */
+ if (errno == 0)
+# ifdef ECONNRESET
+ errno = ECONNRESET;
+# else /* ECONNRESET */
+ errno = EPIPE;
+# endif /* ECONNRESET */
+
+ mci->mci_errno = errno;
+ oldholderrs = HoldErrs;
+ HoldErrs = TRUE;
+ usrerr("451 reply: read error from %s", CurHostName);
+ mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
+
+ /* if debugging, pause so we can see state */
+ if (tTd(18, 100))
+ pause();
+ mci->mci_state = MCIS_ERROR;
+ smtpquit(m, mci, e);
+#if XDEBUG
+ {
+ char wbuf[MAXLINE];
+ char *p = wbuf;
+ int wbufleft = sizeof wbuf;
+
+ if (e->e_to != NULL)
+ {
+ int plen;
+
+ snprintf(p, wbufleft, "%s... ",
+ shortenstring(e->e_to, MAXSHORTSTR));
+ plen = strlen(p);
+ p += plen;
+ wbufleft -= plen;
+ }
+ snprintf(p, wbufleft, "reply(%.100s) during %s",
+ CurHostName, SmtpPhase);
+ checkfd012(wbuf);
+ }
+#endif
+ HoldErrs = oldholderrs;
+ return (-1);
+ }
+ fixcrlf(bufp, TRUE);
+
+ /* EHLO failure is not a real error */
+ if (e->e_xfp != NULL && (bufp[0] == '4' ||
+ (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
+ {
+ /* serious error -- log the previous command */
+ if (SmtpNeedIntro)
+ {
+ /* inform user who we are chatting with */
+ fprintf(CurEnv->e_xfp,
+ "... while talking to %s:\n",
+ CurHostName);
+ SmtpNeedIntro = FALSE;
+ }
+ if (SmtpMsgBuffer[0] != '\0')
+ fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
+ SmtpMsgBuffer[0] = '\0';
+
+ /* now log the message as from the other side */
+ fprintf(e->e_xfp, "<<< %s\n", bufp);
+ }
+
+ /* display the input for verbose mode */
+ if (Verbose)
+ nmessage("050 %s", bufp);
+
+ /* ignore improperly formated input */
+ if (!(isascii(bufp[0]) && isdigit(bufp[0])) ||
+ !(isascii(bufp[1]) && isdigit(bufp[1])) ||
+ !(isascii(bufp[2]) && isdigit(bufp[2])) ||
+ !(bufp[3] == ' ' || bufp[3] == '-' || bufp[3] == '\0'))
+ continue;
+
+ /* process the line */
+ if (pfunc != NULL)
+ (*pfunc)(bufp, firstline, m, mci, e);
+
+ firstline = FALSE;
+
+ /* decode the reply code */
+ r = atoi(bufp);
+
+ /* extra semantics: 0xx codes are "informational" */
+ if (r < 100)
+ continue;
+
+ /* if no continuation lines, return this line */
+ if (bufp[3] != '-')
+ break;
+
+ /* first line of real reply -- ignore rest */
+ bufp = junkbuf;
+ }
+
+ /*
+ ** Now look at SmtpReplyBuffer -- only care about the first
+ ** line of the response from here on out.
+ */
+
+ /* save temporary failure messages for posterity */
+ if (SmtpReplyBuffer[0] == '4' &&
+ (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0'))
+ snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer);
+
+ /* reply code 421 is "Service Shutting Down" */
+ if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
+ {
+ /* send the quit protocol */
+ mci->mci_state = MCIS_SSD;
+ smtpquit(m, mci, e);
+ }
+
+ return (r);
+}
+ /*
+** SMTPMESSAGE -- send message to server
+**
+** Parameters:
+** f -- format
+** m -- the mailer to control formatting.
+** a, b, c -- parameters
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** writes message to mci->mci_out.
+*/
+
+/*VARARGS1*/
+void
+#ifdef __STDC__
+smtpmessage(char *f, MAILER *m, MCI *mci, ...)
+#else
+smtpmessage(f, m, mci, va_alist)
+ char *f;
+ MAILER *m;
+ MCI *mci;
+ va_dcl
+#endif
+{
+ VA_LOCAL_DECL
+
+ VA_START(mci);
+ (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
+ VA_END;
+
+ if (tTd(18, 1) || Verbose)
+ nmessage(">>> %s", SmtpMsgBuffer);
+ if (TrafficLogFile != NULL)
+ fprintf(TrafficLogFile, "%05d >>> %s\n",
+ (int) getpid(), SmtpMsgBuffer);
+ if (mci->mci_out != NULL)
+ {
+ fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
+ m == NULL ? "\r\n" : m->m_eol);
+ }
+ else if (tTd(18, 1))
+ {
+ printf("smtpmessage: NULL mci_out\n");
+ }
+}
+
+# endif /* SMTP */
diff --git a/contrib/sendmail/src/util.c b/contrib/sendmail/src/util.c
new file mode 100644
index 0000000..ba35dcd
--- /dev/null
+++ b/contrib/sendmail/src/util.c
@@ -0,0 +1,2094 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)util.c 8.159 (Berkeley) 7/1/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include <sysexits.h>
+ /*
+** STRIPQUOTES -- Strip quotes & quote bits from a string.
+**
+** Runs through a string and strips off unquoted quote
+** characters and quote bits. This is done in place.
+**
+** Parameters:
+** s -- the string to strip.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+**
+** Called By:
+** deliver
+*/
+
+void
+stripquotes(s)
+ char *s;
+{
+ register char *p;
+ register char *q;
+ register char c;
+
+ if (s == NULL)
+ return;
+
+ p = q = s;
+ do
+ {
+ c = *p++;
+ if (c == '\\')
+ c = *p++;
+ else if (c == '"')
+ continue;
+ *q++ = c;
+ } while (c != '\0');
+}
+ /*
+** ADDQUOTES -- Adds quotes & quote bits to a string.
+**
+** Runs through a string and adds characters and quote bits.
+**
+** Parameters:
+** s -- the string to modify.
+**
+** Returns:
+** pointer to quoted string.
+**
+** Side Effects:
+** none.
+**
+*/
+
+char *
+addquotes(s)
+ char *s;
+{
+ int len = 0;
+ char c;
+ char *p = s, *q, *r;
+
+ if (s == NULL)
+ return NULL;
+
+ /* Find length of quoted string */
+ while ((c = *p++) != '\0')
+ {
+ len++;
+ if (c == '\\' || c == '"')
+ len++;
+ }
+
+ q = r = xalloc(len + 3);
+ p = s;
+
+ /* add leading quote */
+ *q++ = '"';
+ while ((c = *p++) != '\0')
+ {
+ /* quote \ or " */
+ if (c == '\\' || c == '"')
+ *q++ = '\\';
+ *q++ = c;
+ }
+ *q++ = '"';
+ *q = '\0';
+ return r;
+}
+ /*
+** RFC822_STRING -- Checks string for proper RFC822 string quoting.
+**
+** Runs through a string and verifies RFC822 special characters
+** are only found inside comments, quoted strings, or backslash
+** escaped. Also verified balanced quotes and parenthesis.
+**
+** Parameters:
+** s -- the string to modify.
+**
+** Returns:
+** TRUE -- if the string is RFC822 compliant.
+** FALSE -- if the string is not RFC822 compliant.
+**
+** Side Effects:
+** none.
+**
+*/
+
+bool
+rfc822_string(s)
+ char *s;
+{
+ bool quoted = FALSE;
+ int commentlev = 0;
+ char *c = s;
+
+ if (s == NULL)
+ return FALSE;
+
+ while (*c != '\0')
+ {
+ /* escaped character */
+ if (*c == '\\')
+ {
+ c++;
+ if (*c == '\0')
+ return FALSE;
+ }
+ else if (commentlev == 0 && *c == '"')
+ quoted = !quoted;
+ else if (!quoted)
+ {
+ if (*c == ')')
+ {
+ /* unbalanced ')' */
+ if (commentlev == 0)
+ return FALSE;
+ else
+ commentlev--;
+ }
+ else if (*c == '(')
+ commentlev++;
+ else if (commentlev == 0 &&
+ strchr(MustQuoteChars, *c) != NULL)
+ return FALSE;
+ }
+ c++;
+ }
+ /* unbalanced '"' or '(' */
+ if (quoted || commentlev != 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+ /*
+** XALLOC -- Allocate memory and bitch wildly on failure.
+**
+** THIS IS A CLUDGE. This should be made to give a proper
+** error -- but after all, what can we do?
+**
+** Parameters:
+** sz -- size of area to allocate.
+**
+** Returns:
+** pointer to data region.
+**
+** Side Effects:
+** Memory is allocated.
+*/
+
+char *
+xalloc(sz)
+ register int sz;
+{
+ register char *p;
+
+ /* some systems can't handle size zero mallocs */
+ if (sz <= 0)
+ sz = 1;
+
+ p = malloc((unsigned) sz);
+ if (p == NULL)
+ {
+ syserr("!Out of memory!!");
+ /* exit(EX_UNAVAILABLE); */
+ }
+ return (p);
+}
+ /*
+** COPYPLIST -- copy list of pointers.
+**
+** This routine is the equivalent of newstr for lists of
+** pointers.
+**
+** Parameters:
+** list -- list of pointers to copy.
+** Must be NULL terminated.
+** copycont -- if TRUE, copy the contents of the vector
+** (which must be a string) also.
+**
+** Returns:
+** a copy of 'list'.
+**
+** Side Effects:
+** none.
+*/
+
+char **
+copyplist(list, copycont)
+ char **list;
+ bool copycont;
+{
+ register char **vp;
+ register char **newvp;
+
+ for (vp = list; *vp != NULL; vp++)
+ continue;
+
+ vp++;
+
+ newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
+ bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
+
+ if (copycont)
+ {
+ for (vp = newvp; *vp != NULL; vp++)
+ *vp = newstr(*vp);
+ }
+
+ return (newvp);
+}
+ /*
+** COPYQUEUE -- copy address queue.
+**
+** This routine is the equivalent of newstr for address queues
+** addresses marked with QDONTSEND aren't copied
+**
+** Parameters:
+** addr -- list of address structures to copy.
+**
+** Returns:
+** a copy of 'addr'.
+**
+** Side Effects:
+** none.
+*/
+
+ADDRESS *
+copyqueue(addr)
+ ADDRESS *addr;
+{
+ register ADDRESS *newaddr;
+ ADDRESS *ret;
+ register ADDRESS **tail = &ret;
+
+ while (addr != NULL)
+ {
+ if (!bitset(QDONTSEND, addr->q_flags))
+ {
+ newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
+ STRUCTCOPY(*addr, *newaddr);
+ *tail = newaddr;
+ tail = &newaddr->q_next;
+ }
+ addr = addr->q_next;
+ }
+ *tail = NULL;
+
+ return ret;
+}
+ /*
+** PRINTAV -- print argument vector.
+**
+** Parameters:
+** av -- argument vector.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** prints av.
+*/
+
+void
+printav(av)
+ register char **av;
+{
+ while (*av != NULL)
+ {
+ if (tTd(0, 44))
+ printf("\n\t%08lx=", (u_long) *av);
+ else
+ (void) putchar(' ');
+ xputs(*av++);
+ }
+ (void) putchar('\n');
+}
+ /*
+** LOWER -- turn letter into lower case.
+**
+** Parameters:
+** c -- character to turn into lower case.
+**
+** Returns:
+** c, in lower case.
+**
+** Side Effects:
+** none.
+*/
+
+char
+lower(c)
+ register char c;
+{
+ return((isascii(c) && isupper(c)) ? tolower(c) : c);
+}
+ /*
+** XPUTS -- put string doing control escapes.
+**
+** Parameters:
+** s -- string to put.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** output to stdout
+*/
+
+void
+xputs(s)
+ register const char *s;
+{
+ register int c;
+ register struct metamac *mp;
+ bool shiftout = FALSE;
+ extern struct metamac MetaMacros[];
+
+ if (s == NULL)
+ {
+ printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off);
+ return;
+ }
+ while ((c = (*s++ & 0377)) != '\0')
+ {
+ if (shiftout)
+ {
+ printf("%s", TermEscape.te_rv_off);
+ shiftout = FALSE;
+ }
+ if (!isascii(c))
+ {
+ if (c == MATCHREPL)
+ {
+ printf("%s$", TermEscape.te_rv_on);
+ shiftout = TRUE;
+ if (*s == '\0')
+ continue;
+ c = *s++ & 0377;
+ goto printchar;
+ }
+ if (c == MACROEXPAND || c == MACRODEXPAND)
+ {
+ printf("%s$", TermEscape.te_rv_on);
+ if (c == MACRODEXPAND)
+ putchar('&');
+ shiftout = TRUE;
+ if (*s == '\0')
+ continue;
+ if (strchr("=~&?", *s) != NULL)
+ putchar(*s++);
+ if (bitset(0200, *s))
+ printf("{%s}", macname(*s++ & 0377));
+ else
+ printf("%c", *s++);
+ continue;
+ }
+ for (mp = MetaMacros; mp->metaname != '\0'; mp++)
+ {
+ if ((mp->metaval & 0377) == c)
+ {
+ printf("%s$%c",
+ TermEscape.te_rv_on,
+ mp->metaname);
+ shiftout = TRUE;
+ break;
+ }
+ }
+ if (c == MATCHCLASS || c == MATCHNCLASS)
+ {
+ if (bitset(0200, *s))
+ printf("{%s}", macname(*s++ & 0377));
+ else if (*s != '\0')
+ printf("%c", *s++);
+ }
+ if (mp->metaname != '\0')
+ continue;
+
+ /* unrecognized meta character */
+ printf("%sM-", TermEscape.te_rv_on);
+ shiftout = TRUE;
+ c &= 0177;
+ }
+ printchar:
+ if (isprint(c))
+ {
+ putchar(c);
+ continue;
+ }
+
+ /* wasn't a meta-macro -- find another way to print it */
+ switch (c)
+ {
+ case '\n':
+ c = 'n';
+ break;
+
+ case '\r':
+ c = 'r';
+ break;
+
+ case '\t':
+ c = 't';
+ break;
+ }
+ if (!shiftout)
+ {
+ printf("%s", TermEscape.te_rv_on);
+ shiftout = TRUE;
+ }
+ if (isprint(c))
+ {
+ (void) putchar('\\');
+ (void) putchar(c);
+ }
+ else
+ {
+ (void) putchar('^');
+ (void) putchar(c ^ 0100);
+ }
+ }
+ if (shiftout)
+ printf("%s", TermEscape.te_rv_off);
+ (void) fflush(stdout);
+}
+ /*
+** MAKELOWER -- Translate a line into lower case
+**
+** Parameters:
+** p -- the string to translate. If NULL, return is
+** immediate.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** String pointed to by p is translated to lower case.
+**
+** Called By:
+** parse
+*/
+
+void
+makelower(p)
+ register char *p;
+{
+ register char c;
+
+ if (p == NULL)
+ return;
+ for (; (c = *p) != '\0'; p++)
+ if (isascii(c) && isupper(c))
+ *p = tolower(c);
+}
+ /*
+** BUILDFNAME -- build full name from gecos style entry.
+**
+** This routine interprets the strange entry that would appear
+** in the GECOS field of the password file.
+**
+** Parameters:
+** p -- name to build.
+** login -- the login name of this user (for &).
+** buf -- place to put the result.
+** buflen -- length of buf.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** none.
+*/
+
+void
+buildfname(gecos, login, buf, buflen)
+ register char *gecos;
+ char *login;
+ char *buf;
+ int buflen;
+{
+ register char *p;
+ register char *bp = buf;
+
+ if (*gecos == '*')
+ gecos++;
+
+ /* copy gecos, interpolating & to be full name */
+ for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
+ {
+ if (bp >= &buf[buflen - 1])
+ {
+ /* buffer overflow -- just use login name */
+ snprintf(buf, buflen, "%s", login);
+ return;
+ }
+ if (*p == '&')
+ {
+ /* interpolate full name */
+ snprintf(bp, buflen - (bp - buf), "%s", login);
+ *bp = toupper(*bp);
+ bp += strlen(bp);
+ }
+ else
+ *bp++ = *p;
+ }
+ *bp = '\0';
+}
+ /*
+** FIXCRLF -- fix <CR><LF> in line.
+**
+** Looks for the <CR><LF> combination and turns it into the
+** UNIX canonical <NL> character. It only takes one line,
+** i.e., it is assumed that the first <NL> found is the end
+** of the line.
+**
+** Parameters:
+** line -- the line to fix.
+** stripnl -- if true, strip the newline also.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** line is changed in place.
+*/
+
+void
+fixcrlf(line, stripnl)
+ char *line;
+ bool stripnl;
+{
+ register char *p;
+
+ p = strchr(line, '\n');
+ if (p == NULL)
+ return;
+ if (p > line && p[-1] == '\r')
+ p--;
+ if (!stripnl)
+ *p++ = '\n';
+ *p = '\0';
+}
+ /*
+** PUTLINE -- put a line like fputs obeying SMTP conventions
+**
+** This routine always guarantees outputing a newline (or CRLF,
+** as appropriate) at the end of the string.
+**
+** Parameters:
+** l -- line to put.
+** mci -- the mailer connection information.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** output of l to fp.
+*/
+
+void
+putline(l, mci)
+ register char *l;
+ register MCI *mci;
+{
+ putxline(l, strlen(l), mci, PXLF_MAPFROM);
+}
+ /*
+** PUTXLINE -- putline with flags bits.
+**
+** This routine always guarantees outputing a newline (or CRLF,
+** as appropriate) at the end of the string.
+**
+** Parameters:
+** l -- line to put.
+** len -- the length of the line.
+** mci -- the mailer connection information.
+** pxflags -- flag bits:
+** PXLF_MAPFROM -- map From_ to >From_.
+** PXLF_STRIP8BIT -- strip 8th bit.
+** PXLF_HEADER -- map bare newline in header to newline space.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** output of l to fp.
+*/
+
+void
+putxline(l, len, mci, pxflags)
+ register char *l;
+ size_t len;
+ register MCI *mci;
+ int pxflags;
+{
+ register char *p, *end;
+ int slop = 0;
+ size_t eol_len = strlen(mci->mci_mailer->m_eol);
+
+ /* strip out 0200 bits -- these can look like TELNET protocol */
+ if (bitset(MCIF_7BIT, mci->mci_flags) ||
+ bitset(PXLF_STRIP8BIT, pxflags))
+ {
+ register char svchar;
+
+ for (p = l; (svchar = *p) != '\0'; ++p)
+ if (bitset(0200, svchar))
+ *p = svchar &~ 0200;
+ }
+
+ end = l + len;
+ do
+ {
+ /* find the end of the line */
+ p = memchr(l, '\n', end - l);
+ if (p == NULL)
+ p = end;
+
+ if (TrafficLogFile != NULL)
+ fprintf(TrafficLogFile, "%05d >>> ", (int) getpid());
+
+ /* check for line overflow */
+ while (mci->mci_mailer->m_linelimit > 0 &&
+ (p - l + slop) > mci->mci_mailer->m_linelimit)
+ {
+ char *l_base = l;
+ register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
+
+ if (l[0] == '.' && slop == 0 &&
+ bitnset(M_XDOT, mci->mci_mailer->m_flags))
+ {
+ (void) putc('.', mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen++;
+ if (TrafficLogFile != NULL)
+ (void) putc('.', TrafficLogFile);
+ }
+ else if (l[0] == 'F' && slop == 0 &&
+ bitset(PXLF_MAPFROM, pxflags) &&
+ strncmp(l, "From ", 5) == 0 &&
+ bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
+ {
+ (void) putc('>', mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen++;
+ if (TrafficLogFile != NULL)
+ (void) putc('>', TrafficLogFile);
+ }
+ while (l < q)
+ {
+ (void) putc(*l++, mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen++;
+ }
+ (void) putc('!', mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen++;
+ fputs(mci->mci_mailer->m_eol, mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen += eol_len;
+ (void) putc(' ', mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen++;
+ if (TrafficLogFile != NULL)
+ {
+ for (l = l_base; l < q; l++)
+ (void) putc(*l, TrafficLogFile);
+ fprintf(TrafficLogFile, "!\n%05d >>> ",
+ (int) getpid());
+ }
+ slop = 1;
+ }
+
+ /* output last part */
+ if (l[0] == '.' && slop == 0 &&
+ bitnset(M_XDOT, mci->mci_mailer->m_flags))
+ {
+ (void) putc('.', mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen++;
+ if (TrafficLogFile != NULL)
+ (void) putc('.', TrafficLogFile);
+ }
+ else if (l[0] == 'F' && slop == 0 &&
+ bitset(PXLF_MAPFROM, pxflags) &&
+ strncmp(l, "From ", 5) == 0 &&
+ bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
+ {
+ (void) putc('>', mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen++;
+ if (TrafficLogFile != NULL)
+ (void) putc('>', TrafficLogFile);
+ }
+ for ( ; l < p; ++l)
+ {
+ if (TrafficLogFile != NULL)
+ (void) putc(*l, TrafficLogFile);
+ (void) putc(*l, mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen++;
+ }
+ if (TrafficLogFile != NULL)
+ (void) putc('\n', TrafficLogFile);
+ fputs(mci->mci_mailer->m_eol, mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen += eol_len;
+ if (l < end && *l == '\n')
+ {
+ if (*++l != ' ' && *l != '\t' && *l != '\0' &&
+ bitset(PXLF_HEADER, pxflags))
+ {
+ (void) putc(' ', mci->mci_out);
+ if (!bitset(MCIF_INHEADER, mci->mci_flags))
+ mci->mci_contentlen++;
+ if (TrafficLogFile != NULL)
+ (void) putc(' ', TrafficLogFile);
+ }
+ }
+ } while (l < end);
+}
+ /*
+** XUNLINK -- unlink a file, doing logging as appropriate.
+**
+** Parameters:
+** f -- name of file to unlink.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** f is unlinked.
+*/
+
+void
+xunlink(f)
+ char *f;
+{
+ register int i;
+
+ if (LogLevel > 98)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "unlink %s",
+ f);
+
+ i = unlink(f);
+ if (i < 0 && LogLevel > 97)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "%s: unlink-fail %d",
+ f, errno);
+}
+ /*
+** XFCLOSE -- close a file, doing logging as appropriate.
+**
+** Parameters:
+** fp -- file pointer for the file to close
+** a, b -- miscellaneous crud to print for debugging
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** fp is closed.
+*/
+
+void
+xfclose(fp, a, b)
+ FILE *fp;
+ char *a, *b;
+{
+ if (tTd(53, 99))
+ printf("xfclose(%lx) %s %s\n", (u_long) fp, a, b);
+#if XDEBUG
+ if (fileno(fp) == 1)
+ syserr("xfclose(%s %s): fd = 1", a, b);
+#endif
+ if (fclose(fp) < 0 && tTd(53, 99))
+ printf("xfclose FAILURE: %s\n", errstring(errno));
+}
+ /*
+** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
+**
+** Parameters:
+** buf -- place to put the input line.
+** siz -- size of buf.
+** fp -- file to read from.
+** timeout -- the timeout before error occurs.
+** during -- what we are trying to read (for error messages).
+**
+** Returns:
+** NULL on error (including timeout). This will also leave
+** buf containing a null string.
+** buf otherwise.
+**
+** Side Effects:
+** none.
+*/
+
+static jmp_buf CtxReadTimeout;
+static void readtimeout __P((time_t));
+
+char *
+sfgets(buf, siz, fp, timeout, during)
+ char *buf;
+ int siz;
+ FILE *fp;
+ time_t timeout;
+ char *during;
+{
+ register EVENT *ev = NULL;
+ register char *p;
+
+ if (fp == NULL)
+ {
+ buf[0] = '\0';
+ return NULL;
+ }
+
+ /* set the timeout */
+ if (timeout != 0)
+ {
+ if (setjmp(CtxReadTimeout) != 0)
+ {
+ if (LogLevel > 1)
+ sm_syslog(LOG_NOTICE, CurEnv->e_id,
+ "timeout waiting for input from %.100s during %s",
+ CurHostName ? CurHostName : "local",
+ during);
+ errno = 0;
+ buf[0] = '\0';
+#if XDEBUG
+ checkfd012(during);
+#endif
+ if (TrafficLogFile != NULL)
+ fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n",
+ (int) getpid());
+ return (NULL);
+ }
+ ev = setevent(timeout, readtimeout, 0);
+ }
+
+ /* try to read */
+ p = NULL;
+ while (!feof(fp) && !ferror(fp))
+ {
+ errno = 0;
+ p = fgets(buf, siz, fp);
+ if (p != NULL || errno != EINTR)
+ break;
+ clearerr(fp);
+ }
+
+ /* clear the event if it has not sprung */
+ clrevent(ev);
+
+ /* clean up the books and exit */
+ LineNumber++;
+ if (p == NULL)
+ {
+ buf[0] = '\0';
+ if (TrafficLogFile != NULL)
+ fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid());
+ return (NULL);
+ }
+ if (TrafficLogFile != NULL)
+ fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf);
+ if (SevenBitInput)
+ {
+ for (p = buf; *p != '\0'; p++)
+ *p &= ~0200;
+ }
+ else if (!HasEightBits)
+ {
+ for (p = buf; *p != '\0'; p++)
+ {
+ if (bitset(0200, *p))
+ {
+ HasEightBits = TRUE;
+ break;
+ }
+ }
+ }
+ return (buf);
+}
+
+/* ARGSUSED */
+static void
+readtimeout(timeout)
+ time_t timeout;
+{
+ longjmp(CtxReadTimeout, 1);
+}
+ /*
+** FGETFOLDED -- like fgets, but know about folded lines.
+**
+** Parameters:
+** buf -- place to put result.
+** n -- bytes available.
+** f -- file to read from.
+**
+** Returns:
+** input line(s) on success, NULL on error or EOF.
+** This will normally be buf -- unless the line is too
+** long, when it will be xalloc()ed.
+**
+** Side Effects:
+** buf gets lines from f, with continuation lines (lines
+** with leading white space) appended. CRLF's are mapped
+** into single newlines. Any trailing NL is stripped.
+*/
+
+char *
+fgetfolded(buf, n, f)
+ char *buf;
+ register int n;
+ FILE *f;
+{
+ register char *p = buf;
+ char *bp = buf;
+ register int i;
+
+ n--;
+ while ((i = getc(f)) != EOF)
+ {
+ if (i == '\r')
+ {
+ i = getc(f);
+ if (i != '\n')
+ {
+ if (i != EOF)
+ (void) ungetc(i, f);
+ i = '\r';
+ }
+ }
+ if (--n <= 0)
+ {
+ /* allocate new space */
+ char *nbp;
+ int nn;
+
+ nn = (p - bp);
+ if (nn < MEMCHUNKSIZE)
+ nn *= 2;
+ else
+ nn += MEMCHUNKSIZE;
+ nbp = xalloc(nn);
+ bcopy(bp, nbp, p - bp);
+ p = &nbp[p - bp];
+ if (bp != buf)
+ free(bp);
+ bp = nbp;
+ n = nn - (p - bp);
+ }
+ *p++ = i;
+ if (i == '\n')
+ {
+ LineNumber++;
+ i = getc(f);
+ if (i != EOF)
+ (void) ungetc(i, f);
+ if (i != ' ' && i != '\t')
+ break;
+ }
+ }
+ if (p == bp)
+ return (NULL);
+ if (p[-1] == '\n')
+ p--;
+ *p = '\0';
+ return (bp);
+}
+ /*
+** CURTIME -- return current time.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** the current time.
+**
+** Side Effects:
+** none.
+*/
+
+time_t
+curtime()
+{
+ auto time_t t;
+
+ (void) time(&t);
+ return (t);
+}
+ /*
+** ATOBOOL -- convert a string representation to boolean.
+**
+** Defaults to "TRUE"
+**
+** Parameters:
+** s -- string to convert. Takes "tTyY" as true,
+** others as false.
+**
+** Returns:
+** A boolean representation of the string.
+**
+** Side Effects:
+** none.
+*/
+
+bool
+atobool(s)
+ register char *s;
+{
+ if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
+ return (TRUE);
+ return (FALSE);
+}
+ /*
+** ATOOCT -- convert a string representation to octal.
+**
+** Parameters:
+** s -- string to convert.
+**
+** Returns:
+** An integer representing the string interpreted as an
+** octal number.
+**
+** Side Effects:
+** none.
+*/
+
+int
+atooct(s)
+ register char *s;
+{
+ register int i = 0;
+
+ while (*s >= '0' && *s <= '7')
+ i = (i << 3) | (*s++ - '0');
+ return (i);
+}
+ /*
+** BITINTERSECT -- tell if two bitmaps intersect
+**
+** Parameters:
+** a, b -- the bitmaps in question
+**
+** Returns:
+** TRUE if they have a non-null intersection
+** FALSE otherwise
+**
+** Side Effects:
+** none.
+*/
+
+bool
+bitintersect(a, b)
+ BITMAP a;
+ BITMAP b;
+{
+ int i;
+
+ for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+ if ((a[i] & b[i]) != 0)
+ return (TRUE);
+ return (FALSE);
+}
+ /*
+** BITZEROP -- tell if a bitmap is all zero
+**
+** Parameters:
+** map -- the bit map to check
+**
+** Returns:
+** TRUE if map is all zero.
+** FALSE if there are any bits set in map.
+**
+** Side Effects:
+** none.
+*/
+
+bool
+bitzerop(map)
+ BITMAP map;
+{
+ int i;
+
+ for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+ if (map[i] != 0)
+ return (FALSE);
+ return (TRUE);
+}
+ /*
+** STRCONTAINEDIN -- tell if one string is contained in another
+**
+** Parameters:
+** a -- possible substring.
+** b -- possible superstring.
+**
+** Returns:
+** TRUE if a is contained in b.
+** FALSE otherwise.
+*/
+
+bool
+strcontainedin(a, b)
+ register char *a;
+ register char *b;
+{
+ int la;
+ int lb;
+ int c;
+
+ la = strlen(a);
+ lb = strlen(b);
+ c = *a;
+ if (isascii(c) && isupper(c))
+ c = tolower(c);
+ for (; lb-- >= la; b++)
+ {
+ if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
+ continue;
+ if (strncasecmp(a, b, la) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+ /*
+** CHECKFD012 -- check low numbered file descriptors
+**
+** File descriptors 0, 1, and 2 should be open at all times.
+** This routine verifies that, and fixes it if not true.
+**
+** Parameters:
+** where -- a tag printed if the assertion failed
+**
+** Returns:
+** none
+*/
+
+void
+checkfd012(where)
+ char *where;
+{
+#if XDEBUG
+ register int i;
+
+ for (i = 0; i < 3; i++)
+ fill_fd(i, where);
+#endif /* XDEBUG */
+}
+ /*
+** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
+**
+** Parameters:
+** fd -- file descriptor to check.
+** where -- tag to print on failure.
+**
+** Returns:
+** none.
+*/
+
+void
+checkfdopen(fd, where)
+ int fd;
+ char *where;
+{
+#if XDEBUG
+ struct stat st;
+
+ if (fstat(fd, &st) < 0 && errno == EBADF)
+ {
+ syserr("checkfdopen(%d): %s not open as expected!", fd, where);
+ printopenfds(TRUE);
+ }
+#endif
+}
+ /*
+** CHECKFDS -- check for new or missing file descriptors
+**
+** Parameters:
+** where -- tag for printing. If null, take a base line.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** If where is set, shows changes since the last call.
+*/
+
+void
+checkfds(where)
+ char *where;
+{
+ int maxfd;
+ register int fd;
+ bool printhdr = TRUE;
+ int save_errno = errno;
+ static BITMAP baseline;
+ extern int DtableSize;
+
+ if (DtableSize > 256)
+ maxfd = 256;
+ else
+ maxfd = DtableSize;
+ if (where == NULL)
+ clrbitmap(baseline);
+
+ for (fd = 0; fd < maxfd; fd++)
+ {
+ struct stat stbuf;
+
+ if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
+ {
+ if (!bitnset(fd, baseline))
+ continue;
+ clrbitn(fd, baseline);
+ }
+ else if (!bitnset(fd, baseline))
+ setbitn(fd, baseline);
+ else
+ continue;
+
+ /* file state has changed */
+ if (where == NULL)
+ continue;
+ if (printhdr)
+ {
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "%s: changed fds:",
+ where);
+ printhdr = FALSE;
+ }
+ dumpfd(fd, TRUE, TRUE);
+ }
+ errno = save_errno;
+}
+ /*
+** PRINTOPENFDS -- print the open file descriptors (for debugging)
+**
+** Parameters:
+** logit -- if set, send output to syslog; otherwise
+** print for debugging.
+**
+** Returns:
+** none.
+*/
+
+#include <arpa/inet.h>
+
+void
+printopenfds(logit)
+ bool logit;
+{
+ register int fd;
+ extern int DtableSize;
+
+ for (fd = 0; fd < DtableSize; fd++)
+ dumpfd(fd, FALSE, logit);
+}
+ /*
+** DUMPFD -- dump a file descriptor
+**
+** Parameters:
+** fd -- the file descriptor to dump.
+** printclosed -- if set, print a notification even if
+** it is closed; otherwise print nothing.
+** logit -- if set, send output to syslog instead of stdout.
+*/
+
+void
+dumpfd(fd, printclosed, logit)
+ int fd;
+ bool printclosed;
+ bool logit;
+{
+ register char *p;
+ char *hp;
+#ifdef S_IFSOCK
+ SOCKADDR sa;
+#endif
+ auto SOCKADDR_LEN_T slen;
+ int i;
+#if STAT64 > 0
+ struct stat64 st;
+#else
+ struct stat st;
+#endif
+ char buf[200];
+
+ p = buf;
+ snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
+ p += strlen(p);
+
+ if (
+#if STAT64 > 0
+ fstat64(fd, &st)
+#else
+ fstat(fd, &st)
+#endif
+ < 0)
+ {
+ if (errno != EBADF)
+ {
+ snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)",
+ errstring(errno));
+ goto printit;
+ }
+ else if (printclosed)
+ {
+ snprintf(p, SPACELEFT(buf, p), "CLOSED");
+ goto printit;
+ }
+ return;
+ }
+
+ i = fcntl(fd, F_GETFL, NULL);
+ if (i != -1)
+ {
+ snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
+ p += strlen(p);
+ }
+
+ snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode);
+ p += strlen(p);
+ switch (st.st_mode & S_IFMT)
+ {
+#ifdef S_IFSOCK
+ case S_IFSOCK:
+ snprintf(p, SPACELEFT(buf, p), "SOCK ");
+ p += strlen(p);
+ slen = sizeof sa;
+ if (getsockname(fd, &sa.sa, &slen) < 0)
+ snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
+ else
+ {
+ hp = hostnamebyanyaddr(&sa);
+ if (sa.sa.sa_family == AF_INET)
+ snprintf(p, SPACELEFT(buf, p), "%s/%d",
+ hp, ntohs(sa.sin.sin_port));
+ else
+ snprintf(p, SPACELEFT(buf, p), "%s", hp);
+ }
+ p += strlen(p);
+ snprintf(p, SPACELEFT(buf, p), "->");
+ p += strlen(p);
+ slen = sizeof sa;
+ if (getpeername(fd, &sa.sa, &slen) < 0)
+ snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
+ else
+ {
+ hp = hostnamebyanyaddr(&sa);
+ if (sa.sa.sa_family == AF_INET)
+ snprintf(p, SPACELEFT(buf, p), "%s/%d",
+ hp, ntohs(sa.sin.sin_port));
+ else
+ snprintf(p, SPACELEFT(buf, p), "%s", hp);
+ }
+ break;
+#endif
+
+ case S_IFCHR:
+ snprintf(p, SPACELEFT(buf, p), "CHR: ");
+ p += strlen(p);
+ goto defprint;
+
+ case S_IFBLK:
+ snprintf(p, SPACELEFT(buf, p), "BLK: ");
+ p += strlen(p);
+ goto defprint;
+
+#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
+ case S_IFIFO:
+ snprintf(p, SPACELEFT(buf, p), "FIFO: ");
+ p += strlen(p);
+ goto defprint;
+#endif
+
+#ifdef S_IFDIR
+ case S_IFDIR:
+ snprintf(p, SPACELEFT(buf, p), "DIR: ");
+ p += strlen(p);
+ goto defprint;
+#endif
+
+#ifdef S_IFLNK
+ case S_IFLNK:
+ snprintf(p, SPACELEFT(buf, p), "LNK: ");
+ p += strlen(p);
+ goto defprint;
+#endif
+
+ default:
+defprint:
+ if (sizeof st.st_ino > sizeof (long))
+ snprintf(p, SPACELEFT(buf, p),
+ "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ",
+ major(st.st_dev), minor(st.st_dev),
+ quad_to_string(st.st_ino),
+ st.st_nlink, st.st_uid, st.st_gid);
+ else
+ snprintf(p, SPACELEFT(buf, p),
+ "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ",
+ major(st.st_dev), minor(st.st_dev),
+ (unsigned long) st.st_ino,
+ st.st_nlink, st.st_uid, st.st_gid);
+ if (sizeof st.st_size > sizeof (long))
+ snprintf(p, SPACELEFT(buf, p), "size=%s",
+ quad_to_string(st.st_size));
+ else
+ snprintf(p, SPACELEFT(buf, p), "size=%lu",
+ (unsigned long) st.st_size);
+ break;
+ }
+
+printit:
+ if (logit)
+ sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
+ "%.800s", buf);
+ else
+ printf("%s\n", buf);
+}
+ /*
+** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
+**
+** Parameters:
+** host -- the host to shorten (stripped in place).
+**
+** Returns:
+** none.
+*/
+
+void
+shorten_hostname(host)
+ char host[];
+{
+ register char *p;
+ char *mydom;
+ int i;
+ bool canon = FALSE;
+
+ /* strip off final dot */
+ p = &host[strlen(host) - 1];
+ if (*p == '.')
+ {
+ *p = '\0';
+ canon = TRUE;
+ }
+
+ /* see if there is any domain at all -- if not, we are done */
+ p = strchr(host, '.');
+ if (p == NULL)
+ return;
+
+ /* yes, we have a domain -- see if it looks like us */
+ mydom = macvalue('m', CurEnv);
+ if (mydom == NULL)
+ mydom = "";
+ i = strlen(++p);
+ if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
+ (mydom[i] == '.' || mydom[i] == '\0'))
+ *--p = '\0';
+}
+ /*
+** PROG_OPEN -- open a program for reading
+**
+** Parameters:
+** argv -- the argument list.
+** pfd -- pointer to a place to store the file descriptor.
+** e -- the current envelope.
+**
+** Returns:
+** pid of the process -- -1 if it failed.
+*/
+
+int
+prog_open(argv, pfd, e)
+ char **argv;
+ int *pfd;
+ ENVELOPE *e;
+{
+ int pid;
+ int i;
+ int saveerrno;
+ int fdv[2];
+ char *p, *q;
+ char buf[MAXLINE + 1];
+ extern int DtableSize;
+
+ if (pipe(fdv) < 0)
+ {
+ syserr("%s: cannot create pipe for stdout", argv[0]);
+ return -1;
+ }
+ pid = fork();
+ if (pid < 0)
+ {
+ syserr("%s: cannot fork", argv[0]);
+ close(fdv[0]);
+ close(fdv[1]);
+ return -1;
+ }
+ if (pid > 0)
+ {
+ /* parent */
+ close(fdv[1]);
+ *pfd = fdv[0];
+ return pid;
+ }
+
+ /* child -- close stdin */
+ close(0);
+
+ /* stdout goes back to parent */
+ close(fdv[0]);
+ if (dup2(fdv[1], 1) < 0)
+ {
+ syserr("%s: cannot dup2 for stdout", argv[0]);
+ _exit(EX_OSERR);
+ }
+ close(fdv[1]);
+
+ /* stderr goes to transcript if available */
+ if (e->e_xfp != NULL)
+ {
+ if (dup2(fileno(e->e_xfp), 2) < 0)
+ {
+ syserr("%s: cannot dup2 for stderr", argv[0]);
+ _exit(EX_OSERR);
+ }
+ }
+
+ /* this process has no right to the queue file */
+ if (e->e_lockfp != NULL)
+ close(fileno(e->e_lockfp));
+
+ /* run as default user */
+ endpwent();
+ if (setgid(DefGid) < 0 && geteuid() == 0)
+ syserr("prog_open: setgid(%ld) failed", (long) DefGid);
+ if (setuid(DefUid) < 0 && geteuid() == 0)
+ syserr("prog_open: setuid(%ld) failed", (long) DefUid);
+
+ /* run in some directory */
+ if (ProgMailer != NULL)
+ p = ProgMailer->m_execdir;
+ else
+ p = NULL;
+ for (; p != NULL; p = q)
+ {
+ q = strchr(p, ':');
+ if (q != NULL)
+ *q = '\0';
+ expand(p, buf, sizeof buf, e);
+ if (q != NULL)
+ *q++ = ':';
+ if (buf[0] != '\0' && chdir(buf) >= 0)
+ break;
+ }
+ if (p == NULL)
+ {
+ /* backup directories */
+ if (chdir("/tmp") < 0)
+ (void) chdir("/");
+ }
+
+ /* arrange for all the files to be closed */
+ for (i = 3; i < DtableSize; i++)
+ {
+ register int j;
+
+ if ((j = fcntl(i, F_GETFD, 0)) != -1)
+ (void) fcntl(i, F_SETFD, j | 1);
+ }
+
+ /* now exec the process */
+ execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
+
+ /* woops! failed */
+ saveerrno = errno;
+ syserr("%s: cannot exec", argv[0]);
+ if (transienterror(saveerrno))
+ _exit(EX_OSERR);
+ _exit(EX_CONFIG);
+ return -1; /* avoid compiler warning on IRIX */
+}
+ /*
+** GET_COLUMN -- look up a Column in a line buffer
+**
+** Parameters:
+** line -- the raw text line to search.
+** col -- the column number to fetch.
+** delim -- the delimiter between columns. If null,
+** use white space.
+** buf -- the output buffer.
+** buflen -- the length of buf.
+**
+** Returns:
+** buf if successful.
+** NULL otherwise.
+*/
+
+char *
+get_column(line, col, delim, buf, buflen)
+ char line[];
+ int col;
+ char delim;
+ char buf[];
+ int buflen;
+{
+ char *p;
+ char *begin, *end;
+ int i;
+ char delimbuf[4];
+
+ if (delim == '\0')
+ strcpy(delimbuf, "\n\t ");
+ else
+ {
+ delimbuf[0] = delim;
+ delimbuf[1] = '\0';
+ }
+
+ p = line;
+ if (*p == '\0')
+ return NULL; /* line empty */
+ if (*p == delim && col == 0)
+ return NULL; /* first column empty */
+
+ begin = line;
+
+ if (col == 0 && delim == '\0')
+ {
+ while (*begin != '\0' && isascii(*begin) && isspace(*begin))
+ begin++;
+ }
+
+ for (i = 0; i < col; i++)
+ {
+ if ((begin = strpbrk(begin, delimbuf)) == NULL)
+ return NULL; /* no such column */
+ begin++;
+ if (delim == '\0')
+ {
+ while (*begin != '\0' && isascii(*begin) && isspace(*begin))
+ begin++;
+ }
+ }
+
+ end = strpbrk(begin, delimbuf);
+ if (end == NULL)
+ i = strlen(begin);
+ else
+ i = end - begin;
+ if (i >= buflen)
+ i = buflen - 1;
+ strncpy(buf, begin, i);
+ buf[i] = '\0';
+ return buf;
+}
+ /*
+** CLEANSTRCPY -- copy string keeping out bogus characters
+**
+** Parameters:
+** t -- "to" string.
+** f -- "from" string.
+** l -- length of space available in "to" string.
+**
+** Returns:
+** none.
+*/
+
+void
+cleanstrcpy(t, f, l)
+ register char *t;
+ register char *f;
+ int l;
+{
+ /* check for newlines and log if necessary */
+ (void) denlstring(f, TRUE, TRUE);
+
+ l--;
+ while (l > 0 && *f != '\0')
+ {
+ if (isascii(*f) &&
+ (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
+ {
+ l--;
+ *t++ = *f;
+ }
+ f++;
+ }
+ *t = '\0';
+}
+ /*
+** DENLSTRING -- convert newlines in a string to spaces
+**
+** Parameters:
+** s -- the input string
+** strict -- if set, don't permit continuation lines.
+** logattacks -- if set, log attempted attacks.
+**
+** Returns:
+** A pointer to a version of the string with newlines
+** mapped to spaces. This should be copied.
+*/
+
+char *
+denlstring(s, strict, logattacks)
+ char *s;
+ bool strict;
+ bool logattacks;
+{
+ register char *p;
+ int l;
+ static char *bp = NULL;
+ static int bl = 0;
+
+ p = s;
+ while ((p = strchr(p, '\n')) != NULL)
+ if (strict || (*++p != ' ' && *p != '\t'))
+ break;
+ if (p == NULL)
+ return s;
+
+ l = strlen(s) + 1;
+ if (bl < l)
+ {
+ /* allocate more space */
+ if (bp != NULL)
+ free(bp);
+ bp = xalloc(l);
+ bl = l;
+ }
+ strcpy(bp, s);
+ for (p = bp; (p = strchr(p, '\n')) != NULL; )
+ *p++ = ' ';
+
+ if (logattacks)
+ {
+ sm_syslog(LOG_NOTICE, CurEnv->e_id,
+ "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
+ RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
+ shortenstring(bp, MAXSHORTSTR));
+ }
+
+ return bp;
+}
+ /*
+** PATH_IS_DIR -- check to see if file exists and is a directory.
+**
+** There are some additional checks for security violations in
+** here. This routine is intended to be used for the host status
+** support.
+**
+** Parameters:
+** pathname -- pathname to check for directory-ness.
+** createflag -- if set, create directory if needed.
+**
+** Returns:
+** TRUE -- if the indicated pathname is a directory
+** FALSE -- otherwise
+*/
+
+int
+path_is_dir(pathname, createflag)
+ char *pathname;
+ bool createflag;
+{
+ struct stat statbuf;
+
+#if HASLSTAT
+ if (lstat(pathname, &statbuf) < 0)
+#else
+ if (stat(pathname, &statbuf) < 0)
+#endif
+ {
+ if (errno != ENOENT || !createflag)
+ return FALSE;
+ if (mkdir(pathname, 0755) < 0)
+ return FALSE;
+ return TRUE;
+ }
+ if (!S_ISDIR(statbuf.st_mode))
+ {
+ errno = ENOTDIR;
+ return FALSE;
+ }
+
+ /* security: don't allow writable directories */
+ if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
+ {
+ errno = EACCES;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+ /*
+** PROC_LIST_ADD -- add process id to list of our children
+**
+** Parameters:
+** pid -- pid to add to list.
+**
+** Returns:
+** none
+*/
+
+static pid_t *ProcListVec = NULL;
+static int ProcListSize = 0;
+
+#define NO_PID ((pid_t) 0)
+#ifndef PROC_LIST_SEG
+# define PROC_LIST_SEG 32 /* number of pids to alloc at a time */
+#endif
+
+void
+proc_list_add(pid)
+ pid_t pid;
+{
+ int i;
+ extern void proc_list_probe __P((void));
+
+ for (i = 0; i < ProcListSize; i++)
+ {
+ if (ProcListVec[i] == NO_PID)
+ break;
+ }
+ if (i >= ProcListSize)
+ {
+ /* probe the existing vector to avoid growing infinitely */
+ proc_list_probe();
+
+ /* now scan again */
+ for (i = 0; i < ProcListSize; i++)
+ {
+ if (ProcListVec[i] == NO_PID)
+ break;
+ }
+ }
+ if (i >= ProcListSize)
+ {
+ /* grow process list */
+ pid_t *npv;
+
+ npv = (pid_t *) xalloc(sizeof (pid_t) * (ProcListSize + PROC_LIST_SEG));
+ if (ProcListSize > 0)
+ {
+ bcopy(ProcListVec, npv, ProcListSize * sizeof (pid_t));
+ free(ProcListVec);
+ }
+ for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
+ npv[i] = NO_PID;
+ i = ProcListSize;
+ ProcListSize += PROC_LIST_SEG;
+ ProcListVec = npv;
+ }
+ ProcListVec[i] = pid;
+ CurChildren++;
+}
+ /*
+** PROC_LIST_DROP -- drop pid from process list
+**
+** Parameters:
+** pid -- pid to drop
+**
+** Returns:
+** none.
+*/
+
+void
+proc_list_drop(pid)
+ pid_t pid;
+{
+ int i;
+
+ for (i = 0; i < ProcListSize; i++)
+ {
+ if (ProcListVec[i] == pid)
+ {
+ ProcListVec[i] = NO_PID;
+ break;
+ }
+ }
+ if (CurChildren > 0)
+ CurChildren--;
+}
+ /*
+** PROC_LIST_CLEAR -- clear the process list
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+*/
+
+void
+proc_list_clear()
+{
+ int i;
+
+ for (i = 0; i < ProcListSize; i++)
+ ProcListVec[i] = NO_PID;
+ CurChildren = 0;
+}
+ /*
+** PROC_LIST_PROBE -- probe processes in the list to see if they still exist
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+*/
+
+void
+proc_list_probe()
+{
+ int i;
+
+ for (i = 0; i < ProcListSize; i++)
+ {
+ if (ProcListVec[i] == NO_PID)
+ continue;
+ if (kill(ProcListVec[i], 0) < 0)
+ {
+ if (LogLevel > 3)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "proc_list_probe: lost pid %d",
+ ProcListVec[i]);
+ ProcListVec[i] = NO_PID;
+ CurChildren--;
+ }
+ }
+ if (CurChildren < 0)
+ CurChildren = 0;
+}
+ /*
+** SM_STRCASECMP -- 8-bit clean version of strcasecmp
+**
+** Thank you, vendors, for making this all necessary.
+*/
+
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS 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 REGENTS OR 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison. The mappings are
+ * based upon ascii character sequences.
+ */
+static const u_char charmap[] = {
+ 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
+ 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
+ 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
+ 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
+ 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
+ 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
+ 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
+ 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
+ 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
+ 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
+ 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
+ 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
+ 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
+ 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
+ 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
+};
+
+int
+sm_strcasecmp(s1, s2)
+ const char *s1, *s2;
+{
+ register const u_char *cm = charmap,
+ *us1 = (const u_char *)s1,
+ *us2 = (const u_char *)s2;
+
+ while (cm[*us1] == cm[*us2++])
+ if (*us1++ == '\0')
+ return (0);
+ return (cm[*us1] - cm[*--us2]);
+}
+
+int
+sm_strncasecmp(s1, s2, n)
+ const char *s1, *s2;
+ register size_t n;
+{
+ if (n != 0) {
+ register const u_char *cm = charmap,
+ *us1 = (const u_char *)s1,
+ *us2 = (const u_char *)s2;
+
+ do {
+ if (cm[*us1] != cm[*us2++])
+ return (cm[*us1] - cm[*--us2]);
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+ return (0);
+}
diff --git a/contrib/sendmail/src/version.c b/contrib/sendmail/src/version.c
new file mode 100644
index 0000000..bf0b391
--- /dev/null
+++ b/contrib/sendmail/src/version.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. 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.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)version.c 8.9.1.1 (Berkeley) 7/2/98";
+#endif /* not lint */
+
+char Version[] = "8.9.1";
OpenPOWER on IntegriCloud