Sendmail 8.7.3 from NetBSD
authordm <dm@openbsd.org>
Mon, 29 Jan 1996 01:40:03 +0000 (01:40 +0000)
committerdm <dm@openbsd.org>
Mon, 29 Jan 1996 01:40:03 +0000 (01:40 +0000)
109 files changed:
usr.sbin/sendmail/FAQ
usr.sbin/sendmail/KNOWNBUGS
usr.sbin/sendmail/Makefile
usr.sbin/sendmail/READ_ME
usr.sbin/sendmail/RELEASE_NOTES
usr.sbin/sendmail/cf/README
usr.sbin/sendmail/cf/cf/Makefile
usr.sbin/sendmail/cf/cf/boat-anchor.mc
usr.sbin/sendmail/cf/cf/chez.mc [deleted file]
usr.sbin/sendmail/cf/cf/chimera.mc
usr.sbin/sendmail/cf/cf/clientproto.mc
usr.sbin/sendmail/cf/cf/netbsd-proto.mc
usr.sbin/sendmail/cf/cf/pain.mc
usr.sbin/sendmail/cf/cf/python.mc [deleted file]
usr.sbin/sendmail/cf/cf/sun-lamp.mc
usr.sbin/sendmail/cf/cf/tcpproto.mc
usr.sbin/sendmail/cf/cf/trinity.mc
usr.sbin/sendmail/cf/cf/ucbarpa.mc [deleted file]
usr.sbin/sendmail/cf/cf/ucbvax.mc [deleted file]
usr.sbin/sendmail/cf/cf/uucpproto.mc
usr.sbin/sendmail/cf/cf/vangogh.mc [deleted file]
usr.sbin/sendmail/cf/domain/Berkeley.EDU.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/domain/CS.Berkeley.EDU.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/domain/EECS.Berkeley.EDU.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/domain/S2K.Berkeley.EDU.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/domain/berkeley-only.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/domain/generic.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/feature/bestmx_is_local.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/feature/local_procmail.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/feature/masquerade_envelope.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/feature/notsticky.m4
usr.sbin/sendmail/cf/feature/nullclient.m4
usr.sbin/sendmail/cf/feature/redirect.m4
usr.sbin/sendmail/cf/feature/smrsh.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/feature/stickyhost.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/feature/use_ct_file.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/m4/cf.m4
usr.sbin/sendmail/cf/m4/cfhead.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/m4/nullrelay.m4
usr.sbin/sendmail/cf/m4/proto.m4
usr.sbin/sendmail/cf/m4/version.m4
usr.sbin/sendmail/cf/mailer/cyrus.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/mailer/fax.m4
usr.sbin/sendmail/cf/mailer/local.m4
usr.sbin/sendmail/cf/mailer/mail11.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/mailer/phquery.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/mailer/pop.m4
usr.sbin/sendmail/cf/mailer/procmail.m4 [new file with mode: 0644]
usr.sbin/sendmail/cf/mailer/smtp.m4
usr.sbin/sendmail/cf/mailer/usenet.m4
usr.sbin/sendmail/cf/mailer/uucp.m4
usr.sbin/sendmail/cf/ostype/bsd4.4.m4
usr.sbin/sendmail/cf/sh/makeinfo.sh
usr.sbin/sendmail/contrib/bitdomain.c
usr.sbin/sendmail/contrib/bsdi.mc [new file with mode: 0644]
usr.sbin/sendmail/contrib/expn.pl
usr.sbin/sendmail/contrib/mailprio [new file with mode: 0644]
usr.sbin/sendmail/contrib/rmail.oldsys.patch [new file with mode: 0644]
usr.sbin/sendmail/doc/changes/changes.me
usr.sbin/sendmail/doc/op/op.me
usr.sbin/sendmail/mailstats/mailstats.c
usr.sbin/sendmail/makemap/makemap.8
usr.sbin/sendmail/makemap/makemap.c
usr.sbin/sendmail/src/Makefile
usr.sbin/sendmail/src/Makefile.BSD44
usr.sbin/sendmail/src/Makefile.NetBSD
usr.sbin/sendmail/src/READ_ME
usr.sbin/sendmail/src/TRACEFLAGS
usr.sbin/sendmail/src/alias.c
usr.sbin/sendmail/src/arpadate.c
usr.sbin/sendmail/src/clock.c
usr.sbin/sendmail/src/collect.c
usr.sbin/sendmail/src/conf.c
usr.sbin/sendmail/src/conf.h
usr.sbin/sendmail/src/convtime.c
usr.sbin/sendmail/src/daemon.c
usr.sbin/sendmail/src/deliver.c
usr.sbin/sendmail/src/domain.c
usr.sbin/sendmail/src/envelope.c
usr.sbin/sendmail/src/err.c
usr.sbin/sendmail/src/headers.c
usr.sbin/sendmail/src/macro.c
usr.sbin/sendmail/src/main.c
usr.sbin/sendmail/src/makesendmail
usr.sbin/sendmail/src/map.c
usr.sbin/sendmail/src/mci.c
usr.sbin/sendmail/src/mime.c [new file with mode: 0644]
usr.sbin/sendmail/src/parseaddr.c
usr.sbin/sendmail/src/pathnames.h
usr.sbin/sendmail/src/queue.c
usr.sbin/sendmail/src/readcf.c
usr.sbin/sendmail/src/recipient.c
usr.sbin/sendmail/src/savemail.c
usr.sbin/sendmail/src/sendmail.8
usr.sbin/sendmail/src/sendmail.h
usr.sbin/sendmail/src/sendmail.hf
usr.sbin/sendmail/src/srvrsmtp.c
usr.sbin/sendmail/src/stab.c
usr.sbin/sendmail/src/stats.c
usr.sbin/sendmail/src/sysexits.c
usr.sbin/sendmail/src/trace.c
usr.sbin/sendmail/src/udb.c
usr.sbin/sendmail/src/useful.h
usr.sbin/sendmail/src/usersmtp.c
usr.sbin/sendmail/src/util.c
usr.sbin/sendmail/src/version.c
usr.sbin/sendmail/test/Results
usr.sbin/sendmail/test/t_seteuid.c [new file with mode: 0644]
usr.sbin/sendmail/test/t_setreuid.c

index 743dc36..cc27e50 100644 (file)
+Newsgroups: comp.mail.sendmail,comp.mail.misc,comp.mail.smail,comp.answers,news.answers
+Subject: comp.mail.sendmail Frequently Asked Questions (FAQ)
+From: brad@birch.ims.disa.mil (Brad Knowles)
+Followup-to: comp.mail.sendmail
+Summary: This posting contains a list of Frequently Asked Questions
+    (and their answers) about the program "sendmail", distributed
+    with many versions of Unix (and available for some other
+    operating systems).  This FAQ is shared between
+    comp.mail.sendmail and the Sendmail V8 distribution.  It should
+    be read by anyone who wishes to post to comp.mail.sendmail, or
+    anyone having questions about the newsgroup itself.
+
+Archive-name: mail/sendmail-faq
+Posting-Frequency: monthly (first Monday)
+
+
+[The most recent copy of this document can be obtained via anonymous
+FTP from rtfm.mit.edu in /pub/usenet/news.answers/mail/sendmail-faq.
+If you do not have access to anonymous FTP, you can retrieve it by
+sending email to mail-server@rtfm.mit.edu with the command "send
+usenet/news.answers/mail/sendmail-faq" in the message.]
+
+
+
                            Sendmail Version 8
                        Frequently Asked Questions
-                        Version 8.4 of 4/20/94
+                        Last updated 9/17/95
 
 
-This FAQ is specific to Version 8 of sendmail.  Other questions,
-particularly regarding compilation and configuration, are answered
-in src/READ_ME and cf/README.
+This FAQ is specific to Version 8.6.10 of sendmail.  Other questions,
+particularly regarding compilation and configuration, are answered in
+src/READ_ME and cf/README (found in the V8 sendmail distribution).
 
+This is also the official FAQ for the Usenet newsgroup
+comp.mail.sendmail.
+
+======================================================================
+BEFORE YOU GO ANY FURTHER
+======================================================================
+
+  * What do you wish everyone would do before sending you mail or
+    posting to comp.mail.sendmail?
+
+       Read this FAQ completely.  Read src/READ_ME and cf/README
+       completely.  Read the books written to help with common
+       problems such as compilation and installation, configuration,
+       security issues, etc....  Ask themselves if their question
+       hasn't already been answered.
 ----------------------------------------------------------------------
-  * Where can I get Version 8?
+  * How can I be sure if this is the right place to look for answers
+    to my questions?
 
-       Via anonymous FTP from FTP.CS.Berkeley.EDU in /ucb/sendmail.
+       1. Do you know, for a fact, that the question is related to
+          sendmail V8?
+
+       2. Do you know, for a fact, that the question is related to an
+          older version of sendmail?
+
+       3. Is the question about a sendmail-like program (e.g., Smail,
+          Zmailer, MMDF, etc...)?
+
+       4. Is the question about an SMTP Gateway product for a LAN
+          mail package (e.g., cc:Mail, MS-Mail, WordPerfect
+          Office/GroupWise, etc...)?
+
+       If you answered "yes" to the question #1, then this is the
+       right place.
+
+       If you answered "yes" to questions #2 or #3, then you should
+       seriously consider upgrading to the most recent version of
+       sendmail V8.
+
+       For question #2, If you're going to continue using an older
+       version of sendmail, you may not find much help and will
+       probably get some responses that amount to "Get V8".
+       Otherwise, this is probably the best place to look for
+       answers.
+
+       If you answered "yes" to question #3 and are not going to
+       upgrade to sendmail V8, then this is probably not the right
+       place to look.
+
+       If you answered "yes" to question #4, then this is almost
+       certainly not the right place to look.
+
+       For questions #3 and #4, try looking around elsewhere in the
+       "comp.mail.*" hierarchy for a more appropriate newsgroup.
+       For example, you might want to try posting to comp.mail.misc
+       or comp.mail.smail.
+
+       If you couldn't answer "yes" to any of the above questions,
+       then you're DEFINITELY in the wrong place.  For the sake of
+       your sanity and ego, not to mention avoiding the waste of
+       your time and ours, try asking your System or E-Mail
+       Administrator(s) before you post any questions publicly.
 ----------------------------------------------------------------------
-  * What are the differences between Version 8 and other versions?
+  * Where can I find the latest version of this FAQ?
+
+       It is included in the most recent Version 8 distribution of
+       sendmail (described below), as well as via anonymous FTP from
+       rtfm.mit.edu in /pub/usenet/news.answers/mail/sendmail-faq.
+       If you do not have access to anonymous FTP, you can retrieve
+       it by sending email to mail-server@rtfm.mit.edu with the
+       command "send usenet/news.answers/mail/sendmail-faq" in the
+       message.
+----------------------------------------------------------------------
+  * I don't have access to Usenet news.  Can I still get access to
+    comp.mail.sendmail?
 
-       See doc/changes/changes.me in the sendmail distribution.
+       Yes.  Send email to mxt@dl.ac.uk with the command "sub
+       comp-news.comp.mail.sendmail <full-US-ordered-email-address>"
+       in the message.
+
+       E-mail you want posted on comp.mail.sendmail should be sent
+       to comp-mail-sendmail@dl.ac.uk
 ----------------------------------------------------------------------
-  * What happened to sendmail 6.x and 7.x?
+  * I have sendmail-related DNS questions.  Where should I ask them?
 
-       When I released a new version of sendmail, I changed it to
-       Release 6.  Development continued in that tree until 4.4BSD
-       was released, when everything on the 4.4 tape was set to be
-       version 8.1.  Version 7.x never existed.
+       Depending on how deeply they get into the DNS, they can be
+       asked here.  However, you'll probably be told that you should
+       send them to the Info-BIND mailing list (if the question is
+       specific to that program) or to the Usenet newsgroup
+       comp.protocols.tcp-ip.domains (DNS in general).
 ----------------------------------------------------------------------
-  * Version 8 requires a new version of "make".  Where can I get this?
+  * How do I subscribe to either of these?
 
-       Actually, Version 8 does not require a new version of "make".
-       It includes a collection of Makefiles for different architectures,
-       only one or two of which require the new "make".  If you are
-       porting to a new architecture, start with Makefile.dist.
+       For comp.protocols.tcp-ip.domains, you have to be on Usenet.
+       They don't have a news-to-mail gateway yet.
 
-       If you really do want the new make, it is available on any of
-       the BSD Net2 or 4.4-Lite distribution sites.  These include:
+       For the Info-BIND mailing list, send email to
+       bind-request@uunet.uu.net with the command "subscribe" in the
+       message.  Submissions should be sent to bind@uunet.uu.net
 
-               ftp.uu.net              /systems/unix/bsd-sources
-               gatekeeper.dec.com      /.0/BSD/net2
-               ucquais.cba.uc.edu      /pub/net2
-               ftp.luth.se             /pub/unix/4.3bsd/net2
+======================================================================
+GENERAL QUESTIONS
+======================================================================
 
-       Diffs and instructions for building this version of make under
-       SunOS 4.1.x are available on ftp.css.itd.umich.edu in
-       /pub/systems/sun/Net2-make.sun4.diff.Z.
-----------------------------------------------------------------------
-  * What macro package do I use to format the V8 man pages?
+  * Where can I get Version 8?
 
-       The BSD group switched over the the ``mandoc'' macros for
-       the 4.4 release.  These include more hooks designed for
-       hypertext handling.  However, new man pages won't format
-       under the old man macros.  Fortunately, old man pages will
-       format under the new mandoc macros.
+       Via anonymous FTP from FTP.CS.Berkeley.EDU in /ucb/sendmail.
+----------------------------------------------------------------------
+  * What are the differences between Version 8 and other versions?
 
-       Get the new macros with the BSD Net2 or 4.4-Lite release.
+       See doc/changes/changes.me in the sendmail distribution.
+----------------------------------------------------------------------
+  * What happened to sendmail 6.x and 7.x?
 
-       This macro set is also available with newer versions of groff.
+       When a new (Alpha/Beta) version of sendmail was released, it
+       was changed to Release 6.  Development continued in that tree
+       until 4.4BSD was released, when everything on the 4.4 tape
+       was set to be version 8.1.  Version 7.x never existed.
 ----------------------------------------------------------------------
   * What books are available describing sendmail?
 
@@ -73,7 +169,81 @@ in src/READ_ME and cf/README.
 
            Avolio & Vixie, _Sendmail Theory and Practice_.  Digital
                Press (release date unknown).
+
+       For details on sendmail-related DNS issues, consult:
+
+           Liu and Albitz, _DNS and BIND_.  O'Reilly & Associates.
+
+       For details on UUCP, see:
+
+           O'Reilly and Todino, _Managing UUCP and Usenet_.
+               O'Reilly & Associates.
+
+======================================================================
+COMPILING AND INSTALLING SENDMAIL 8
+======================================================================
+
+  * Version 8 requires a new version of "make".  Where can I get this?
+
+       Actually, Version 8 does not require a new version of "make".
+       It includes a collection of Makefiles for different architectures,
+       only one or two of which require the new "make".  For a supported
+       architecture, use ``sh makesendmail''.  If you are porting to a
+       new architecture, start with Makefile.dist.
+
+       If you really do want the new make, it is available on any of
+       the BSD Net2 or 4.4-Lite distribution sites.  These include:
+
+               ftp.uu.net              /systems/unix/bsd-sources
+               gatekeeper.dec.com      /.0/BSD/net2
+               ucquais.cba.uc.edu      /pub/net2
+               ftp.luth.se             /pub/unix/4.3bsd/net2
+
+       Diffs and instructions for building this version of make
+       under SunOS 4.1.x are available on ftp.css.itd.umich.edu in
+       /pub/systems/sun/Net2-make.sun4.diff.Z.  A patchkit for
+       Ultrix is on ftp.vix.com in /pub/patches/pmake-for-ultrix.Z.
+       Patches for AIX 3.2.4 are available on ftp.uni-stuttgart.de
+       in /sw/src/patches/bsd-make-rus-patches.
+
+       There is also a Linux version available on the main Linux
+       distribution sites as pmake; this version is included as
+       standard with the current Slackware distributions.
+----------------------------------------------------------------------
+  * What macro package do I use to format the V8 man pages?
+
+       The BSD group switched over the the ``mandoc'' macros for the
+       4.4 release.  These include more hooks designed for hypertext
+       handling.  However, new man pages won't format under the old
+       man macros.  Fortunately, old man pages will format under the
+       new mandoc macros.
+
+       Get the new macros with the BSD Net2 or 4.4-Lite release (see
+       above for locations; for example, on FTP.UU.NET the files
+       /system/unix/bsd-sources/share/tmac/me/strip/sed and
+       /system/unix/bsd-sources/share/tmac/* are what you need).
+
+       This macro set is also included with newer versions of groff.
 ----------------------------------------------------------------------
+  * What modes should be used when installing sendmail?
+
+       The sendmail binary should be owned by root, mode 4755.
+       The queue directory should be owned by root, with a mode
+               between 700 and 755.  Under no circumstances should
+               it be group or other writable!
+       The sendmail config file should be owned by root, mode 644.
+       The aliases file should generally be owned by one trusted
+               user and writable only by that user, although it is
+               not unreasonable to have it group writable by a
+               "sysadmin" group.  It should not be world writable.
+       The aliases database files (aliases.db or aliases.{pag,dir}
+               depending on what database format you compile with)
+               should be owned by root, mode 644.
+
+======================================================================
+CONFIGURATION QUESTIONS
+======================================================================
+
   * How do I make all my addresses appear to be from a single host?
 
        Using the V8 configuration macros, use:
@@ -85,11 +255,11 @@ in src/READ_ME and cf/README.
 ----------------------------------------------------------------------
   * How do I rewrite my From: lines to read ``First_Last@My.Domain''?
 
-       There are a couple of ways of doing this.  This describes using
-       the "user database" code.  This is still experimental, and was
-       intended for a different purpose -- however, it does work
-       with a bit of care.  It does require that you have the Berkeley
-       "db" package installed (it won't work with DBM).
+       There are a couple of ways of doing this.  This describes
+       using the "user database" code.  This is still experimental,
+       and was intended for a different purpose -- however, it does
+       work with a bit of care.  It does require that you have the
+       Berkeley "db" package installed (it won't work with DBM).
 
        First, create your input file.  This should have lines like:
 
@@ -108,14 +278,14 @@ in src/READ_ME and cf/README.
 ----------------------------------------------------------------------
   * So what was the user database feature intended for?
 
-       The intent was to have all information for a given user (where
-       the user is the unique login name, not an inherently non-unique
-       full name) in one place.  This would include phone numbers,
-       addresses, and so forth.  The "maildrop" feature is because
-       Berkeley does not use a centralized mail server (there are a
-       number of reasons for this that are mostly historic), and so
-       we need to know where each user gets his or her mail delivered --
-       i.e., the mail drop.
+       The intent was to have all information for a given user
+       (where the user is the unique login name, not an inherently
+       non-unique full name) in one place.  This would include phone
+       numbers, addresses, and so forth.  The "maildrop" feature is
+       because Berkeley does not use a centralized mail server
+       (there are a number of reasons for this that are mostly
+       historic), and so we need to know where each user gets his or
+       her mail delivered -- i.e., the mail drop.
 
        We are in the process of setting up our environment so that
        mail sent to an unqualified "name" goes to that person's
@@ -128,48 +298,33 @@ in src/READ_ME and cf/README.
 
        Because full names are not unique.  For example, the computer
        community has two Andy Tannenbaums and two Peter Deutsches.
-       At one time, Bell Labs had two Stephen R. Bournes with offices
-       a few doors apart.  You can create alternative addresses
-       (e.g., Stephen_R_Bourne_2), but that's even worse -- which
-       one of them has to have their name desecrated in this way?
-       And you can bet that they will get most of the other person's
-       email.
-
-       So called "full names" are just longer versions of unique
-       names.  Rather that lulling people into a sense of security,
-       I'd rather that it be clear that these handles are arbitrary.
-       People should use good user agents that have alias mappings
-       so that they can attach arbitrary names for their personal
-       use to those with whom they correspond.
+       At one time, Bell Labs had two Stephen R. Bournes with
+       offices a few doors apart.  You can create alternative
+       addresses (e.g., Stephen_R_Bourne_2), but that's even worse
+       -- which one of them has to have their name desecrated in
+       this way?  And you can bet that one of them will get most of
+       the other person's e-mail.
+
+       So called "full names" are just an attempt to create longer
+       versions of unique names.  Rather that lulling people into a
+       sense of security, I'd rather that it be clear that these
+       handles are arbitrary.  People should use good user agents
+       that have alias mappings so that they can attach arbitrary
+       names for their personal use to those with whom they
+       correspond (such as the MH alias file).
 
        Even worse is fuzzy matching in e-mail -- this can make good
-       addresses turn bad.  For example, I'm currently (to the best
-       of my knowledge) the only ``Allman'' at Berkeley, so mail
-       sent to "Allman@Berkeley.EDU" should get to me.  But if
-       another Allman ever appears, this address could suddenly
-       become ambiguous.  I've been the only Allman at Berkeley for
-       over fifteen years -- to suddenly have this "good address"
-       bounce mail because it is ambiguous would be a heinous wrong.
-
-       Finger services should be as fuzzy as possible.  Mail services
-       should be unique.
-----------------------------------------------------------------------
-  * When I use sendmail V8 with a Sun config file I get lines like:
-
-       /etc/sendmail.cf: line 273: replacement $3 out of bounds
-
-    the line in question reads:
-
-       R$*<@$%y>$*             $1<@$2.LOCAL>$3                 user@ether
-
-    what does this mean?  How do I fix it?
-
-       V8 doesn't recognize the Sun "$%y" syntax, so as far as it
-       is concerned, there is only a $1 and a $2 (but no $3) in this
-       line.  Read Rick McCarty's paper on "Converting Standard Sun
-       Config Files to Sendmail Version 8", in the contrib directory
-       (file "converting.sun.configs") on the sendmail distribution
-       for a full discussion of how to do this.
+       addresses turn bad.  For example, Eric Allman is currently
+       (to the best of our knowledge) the only ``Allman'' at
+       Berkeley, so mail sent to "Allman@Berkeley.EDU" should get to
+       him.  But if another Allman ever appears, this address could
+       suddenly become ambiguous.  He's been the only Allman at
+       Berkeley for over fifteen years -- to suddenly have this
+       "good address" bounce mail because it is ambiguous would be a
+       heinous wrong.
+
+       Finger services should be as fuzzy as possible (within
+       reason, of course).  Mail services should be unique.
 ----------------------------------------------------------------------
   * Should I use a wildcard MX for my domain?
 
@@ -180,86 +335,78 @@ in src/READ_ME and cf/README.
        you don't explicitly test for unknown hosts in your domain,
        you will get "config error: mail loops back to myself"
        errors.
-----------------------------------------------------------------------
-  * I'm connected to the network via a SLIP link.  Sometimes my sendmail
-    process hangs (although it looks like part of the message has been
-    transfered).  Everything else works.  What's wrong?
 
-       Most likely, the problem isn't sendmail at all, but the low
-       level network connection.  It's important that the MTU (Maximum
-       Transfer Unit) for the SLIP connection be set properly at both
-       ends.  If they disagree, large packets will be trashed and
-       the connection will hang.
+       See RFCs 1535-1537 for more detail and other related (or
+       common) problems.
 ----------------------------------------------------------------------
-  * I just upgraded to 8.x and suddenly I'm getting messages in my
-    syslog of the form "collect: I/O error on connection".  What is
-    going wrong?
+  * How can I get sendmail to process messages sent to an account and
+    send the results back to the originator?
+
+       This is a local mailer issue, not a sendmail issue.
+       Depending on what you're doing, look at procmail (mentioned
+       again below), ftpmail, or Majordomo.
 
-       Nothing.  This is just a diagnosis of a condition that had
-       not been diagnosed before.  If you are getting a lot of these
-       from a single host, there is probably some incompatibility
-       between 8.x and that host.  If you get a lot of them in general,
-       you may have network problems that are causing connections to
-       get reset.
+       Check your local archie server to see what machine(s) nearest
+       you have the most recent versions of these programs.
 ----------------------------------------------------------------------
   * How can I get sendmail to deliver local mail to $HOME/.mail
     instead of into /usr/spool/mail (or /usr/mail)?
 
-       This is a local mailer issue, not a sendmail issue.  Either
-       modify your local mailer (source code will be required) or
-       change the program called in the "local" mailer configuration
-       description to be a new program that does this local delivery.
-       I understand that "procmail" works well, although I haven't
-       used it myself.
-
-       You might be interested in reading the paper ``HLFSD: Delivering
-       Email to your $HOME'' available in the Proceedings of the
-       USENIX System Administration (LISA VII) Conference (November
-       1993).  This is also available via public FTP from
-       ftp.cs.columbia.edu:/pub/hlfsd/{README.hlfsd,hlfsd.ps}.
+       Again, this is a local mailer issue, not a sendmail issue.
+       Either modify your local mailer (source code will be
+       required) or change the program called in the "local" mailer
+       configuration description to be a new program that does this
+       local delivery.  One program that is capable of doing this is
+       "procmail", although there are probably many others as well.
+
+       You might be interested in reading the paper ``HLFSD:
+       Delivering Email to your $HOME'' available in the Proceedings
+       of the USENIX System Administration (LISA VII) Conference
+       (November 1993).  This is also available via public FTP from
+       ftp.cs.columbia.edu in /pub/hlfsd/{README.hlfsd,hlfsd.ps}.
 ----------------------------------------------------------------------
-  * Under V8, the "From " header gets mysteriously munged when I send
-    to an alias.
-
-       ``It's not a bug, it's a feature.''  This happens when you have
-       a "owner-list" alias and you send to "list".  V8 propogates the
-       owner information into the envelope sender field (which appears
-       as the "From " header on UNIX mail or as the Return-Path: header)
-       so that downstream errors are properly returned to the mailing
-       list owner instead of to the sender.  In order to make this
-       appear as sensible as possible to end users, I recommend making
-       the owner point to a "request" address -- for example:
+  * I'm trying to to get my mail to go into queue only mode, and it
+    delivers the mail interactively anyway.  (Or, I'm trying to use
+    the "don't deliver to expensive mailer" flag, and it delivers the
+    mail interactively anyway.)  I can see it does it:  here's the
+    output of "sendmail -v foo@somehost" (or Mail -v or equivalent).
 
-               list:           :include:/path/name/list.list
-               owner-list:     list-request
-               list-request:   eric
+       The -v flag to sendmail (which is implied by the -v flag to
+       Mail and other programs in that family) tells sendmail to
+       watch the transaction.  Since you have explicitly asked to
+       see what's going on, it assumes that you do not want to to
+       auto-queue, and turns that feature off.  Remove the -v flag
+       and use a "tail -f" of the log instead to see what's going
+       on.
 
-       This will make message sent to "list" come out as being
-       "From list-request" instead of "From eric".
+       If you are trying to use the "don't deliver to expensive
+       mailer" flag (mailer flag "e"), be sure you also turn on
+       global option "c" -- otherwise it ignores the mailer flag.
 ----------------------------------------------------------------------
   * There are four UUCP mailers listed in the configuration files.
     Which one should I use?
 
-       The choice is partly a matter of local preferences and what is
-       running at the other end of your UUCP connection.  Unlike good
-       protocols that define what will go over the wire, UUCP uses
-       the policy that you should do what is right for the other end;
-       if they change, you have to change.  This makes it hard to
-       do the right thing, and discourages people from updating their
-       software.  In general, if you can avoid UUCP, please do.
+       The choice is partly a matter of local preferences and what
+       is running at the other end of your UUCP connection.  Unlike
+       good protocols that define what will go over the wire, UUCP
+       uses the policy that you should do what is right for the
+       other end; if they change, you have to change.  This makes it
+       hard to do the right thing, and discourages people from
+       updating their software.  In general, if you can avoid UUCP,
+       please do.
 
-       If you can't avoid it, you'll have to find the version that is
-       closest to what the other end accepts.  Following is a summary
-       of the UUCP mailers available.
+       If you can't avoid it, you'll have to find the version that
+       is closest to what the other end accepts.  Following is a
+       summary of the UUCP mailers available.
 
        uucp-old (obsolete name: "uucp")
-         This is the oldest, the worst (but the closest to UUCP) way of
-         sending messages accros UUCP connections.  It does bangify
-         everything and prepends $U (your UUCP name) to the sender's
-         address (which can already be a bang path itself).  It can
-         only send to one address at a time, so it spends a lot of
-         time copying duplicates of messages.  Avoid this if at all
-         possible.
+         This is the oldest, the worst (but the closest to UUCP) way
+         of sending messages across UUCP connections.  It does
+         bangify everything and prepends $U (your UUCP name) to the
+         sender's address (which can already be a bang path
+         itself).  It can only send to one address at a time, so it
+         spends a lot of time copying duplicates of messages.  Avoid
+         this if at all possible.
 
        uucp-new (obsolete name: "suucp")
          The same as above, except that it assumes that in one rmail
@@ -270,24 +417,25 @@ in src/READ_ME and cf/README.
          This UUCP mailer keeps everything as domain addresses.
          Basically, it uses the SMTP mailer rewriting rules.
 
-         Unfortunately, a lot of UUCP mailer transport agents require
-         bangified addresses in the envelope, although you can use
-         domain-based addresses in the message header.  (The envelope
-         shows up as the From_ line on UNIX mail.)  So....
+         Unfortunately, a lot of UUCP mailer transport agents
+         require bangified addresses in the envelope, although you
+         can use domain-based addresses in the message header.  (The
+         envelope shows up as the From_ line on UNIX mail.)  So....
 
        uucp-uudom
-         This is a cross between uucp-new (for the envelope addresses)
-         and uucp-dom (for the header addresses).  It bangifies the
-         envelope sender (From_ line in messages) without adding the
-         local hostname, unless there is no host name on the address
-         at all (e.g., "wolf") or the host component is a UUCP host name
-         instead of a domain name ("somehost!wolf" instead of
-         "some.dom.ain!wolf").
+         This is a cross between uucp-new (for the envelope
+         addresses) and uucp-dom (for the header addresses).  It
+         bangifies the envelope sender (From_ line in messages)
+         without adding the local hostname, unless there is no host
+         name on the address at all (e.g., "wolf") or the host
+         component is a UUCP host name instead of a domain name
+         ("somehost!wolf" instead of "some.dom.ain!wolf").
 
        Examples:
 
-       We are on host grasp.insa-lyon.fr (UUCP host name "grasp").  The
-       following summarizes the sender rewriting for various mailers.
+       We are on host grasp.insa-lyon.fr (UUCP host name "grasp").
+       The following summarizes the sender rewriting for various
+       mailers.
 
        Mailer          sender          rewriting in the envelope
        ------          ------          -------------------------
@@ -302,24 +450,16 @@ in src/READ_ME and cf/README.
        uucp-{old,new}  somehost!wolf   grasp!somehost!wolf
        uucp-dom        somehost!wolf   somehost!wolf@grasp.insa-lyon.fr
        uucp-uudom      somehost!wolf   grasp.insa-lyon.fr!somehost!wolf
-----------------------------------------------------------------------
-  * I'm trying to to get my mail to go into queue only mode, and it
-    delivers the mail interactively anyway.  (Or, I'm trying to use
-    the "don't deliver to expensive mailer" flag, and it doesn't
-    delivers the mail interactively anyway.)  I can see it does it:
-    here's the output of "sendmail -v foo@somehost" (or Mail -v or
-    equivalent).
 
-       The -v flag to sendmail (which is implied by the -v flag to
-       Mail and other programs in that family) tells sendmail to
-       watch the transaction.  Since you have explicitly asked to
-       see what's going on, it assumes that you do not want to to
-       auto-queue, and turns that feature off.  Remove the -v flag
-       and use a "tail -f" of the log instead to see what's going on.
+======================================================================
+RESOLVING PROBLEMS
+======================================================================
+
+  * When I compile, I get "undefined symbol inet_aton" messages.
 
-       If you are trying to use the "don't deliver to expensive mailer"
-       flag (mailer flag "e"), be sure you also turn on global option
-       "c" -- otherwise it ignores the mailer flag.
+       You've probably replaced your resolver with the version from
+       BIND 4.9.3.  You need to compile with -l44bsd in order to get
+       the additional routines.
 ----------------------------------------------------------------------
   * I'm getting "Local configuration error" messages, such as:
 
@@ -330,14 +470,262 @@ in src/READ_ME and cf/README.
 
        You have asked mail to the domain (e.g., domain.net) to be
        forwarded to a specific host (in this case, relay.domain.net)
-       by using an MX record, but the relay machine doesn't recognize
-       itself as domain.net.  Add domain.net to /etc/sendmail.cw
-       (if you are using FEATURE(use_cw_file)) or add "Cw domain.net"
-       to your configuration file.
+       by using an MX record, but the relay machine doesn't
+       recognize itself as domain.net.  Add domain.net to
+       /etc/sendmail.cw (if you are using FEATURE(use_cw_file)) or
+       add "Cw domain.net" to your configuration file.
+
+       IMPORTANT:  Be sure you kill and restart the sendmail daemon
+       after you change the configuration file (for ANY change in
+       the configuration, not just this one):
+
+               kill `head -1 /etc/sendmail.pid`
+               sh -c "`tail -1 /etc/sendmail.pid`"
+
+       NOTA BENE:  kill -1 does not work!
+----------------------------------------------------------------------
+  * When I use sendmail V8 with a Sun config file I get lines like:
+
+       /etc/sendmail.cf: line 273: replacement $3 out of bounds
+
+    the line in question reads:
+
+       R$*<@$%y>$*             $1<@$2.LOCAL>$3                 user@ether
+
+    what does this mean?  How do I fix it?
+
+       V8 doesn't recognize the Sun "$%y" syntax, so as far as it is
+       concerned, there is only a $1 and a $2 (but no $3) in this
+       line.  Read Rick McCarty's paper on "Converting Standard Sun
+       Config Files to Sendmail Version 8", in the contrib directory
+       (file "converting.sun.configs") on the sendmail distribution
+       for a full discussion of how to do this.
+----------------------------------------------------------------------
+  * When I use sendmail V8 on a Sun, I sometimes get lines like:
+
+       /etc/sendmail.cf: line 445: bad ruleset 96 (50 max)
+
+    what does this mean?  How do I fix it?
+
+       You're somehow trying to start up the old Sun sendmail (or
+       sendmail.mx) with a sendmail V8 config file, which Sun's
+       sendmail doesn't like.  Check your /etc/rc.local, any
+       procedures that have been created to stop and re-start the
+       sendmail processes, etc....  Make sure that you've switched
+       everything over to using the new sendmail.  To keep this
+       problem from ever happening again, try the following:
+
+           mv /usr/lib/sendmail /usr/lib/sendmail.old
+           ln -s /usr/local/lib/sendmail.v8 /usr/lib/sendmail
+           mv /usr/lib/sendmail.mx /usr/lib/sendmail.mx.old
+           ln -s /usr/local/lib/sendmail.v8 /usr/lib/sendmail.mx
+           chmod 0000 /usr/lib/sendmail.old
+           chmod 0000 /usr/lib/sendmail.mx.old
+
+       Assuming you have installed sendmail V8 in /usr/local/lib.
+----------------------------------------------------------------------
+  * When I use sendmail V8 on an IBM RS/6000 running AIX, the system
+    resource controller always reports sendmail as "inoperative" even
+    though it is running.  What's wrong?
+
+       IBM's system resource controller is one of their "value
+       added" features to AIX -- it's not a Unix standard.  You'll
+       need to either redefine the subsystem to use signals (see
+       chssys(1)) or dump the entire subsystem and invoke sendmail
+       in /etc/rc.tcpip or some other boot script.
+----------------------------------------------------------------------
+  * When I use sendmail V8 on an Intel x86 machine running Linux, I
+    have some problems.  Specifically, I have....
+
+       The current versions of Linux are generally considered to be
+       great for hobbyists and anyone else who wants to learn Unix
+       inside and out, or wants to always have something to do, or
+       wants a machine for light-duty mostly personal use and not
+       high-volume multi-user purposes.
+
+       However, for those who want a system that will just sit in
+       the background and work without a fuss handling thousands of
+       mail messages a day for lots of different users, it's not
+       (yet) stable enough to fit the bill.
+
+       Unfortunately, there are no known shareware/freeware
+       implementations of any operating system that provides the
+       level of stability necessary to handle that kind of load
+       (i.e., there are no free lunches).
+
+       If you're wedded to the Intel x86 platform and want to run
+       sendmail, we suggest you look at commercial implementations
+       of Unix such as Interactive, UnixWare, Solaris, or BSD/386
+       (just a sample of the dozens of different versions of Unix
+       for Intel x86).
+
+       Of all known vendor supported versions of Unix for Intel x86,
+       BSDI's BSD/386 is least expensive and the only one known to
+       currently ship with sendmail V8 pre-installed.  Since sendmail
+       V8 is continuing to be developed at UC Berkeley, and BSD/386
+       is a full BSD 4.4 implementation, this is obviously be the most
+       "native" sendmail V8 environment.
+----------------------------------------------------------------------
+  * When I use sendmail on an Intel x86 machine running OS/2, I have
+    some problems.  Specifically, I have....
+
+       The OS/2 port of sendmail is known to have left out huge
+       chunks of the code and functionality of even much older
+       versions of sendmail, in large part because the underlying OS
+       just doesn't have the necessary hooks to make it happen.
+       This port is so broken that we make no attempt to provide any
+       kind of support for it.  Try BSDI's BSD/386 instead.
+----------------------------------------------------------------------
+  * I'm connected to the network via a SLIP/PPP link.  Sometimes my
+    sendmail process hangs (although it looks like part of the
+    message has been transfered).  Everything else works.  What's
+    wrong?
+
+       Most likely, the problem isn't sendmail at all, but the low
+       level network connection.  It's important that the MTU
+       (Maximum Transfer Unit) for the SLIP connection be set
+       properly at both ends.  If they disagree, large packets will
+       be trashed and the connection will hang.
+----------------------------------------------------------------------
+  * I just upgraded to 8.x and suddenly I'm getting messages in my
+    syslog of the form "collect: I/O error on connection".  What is
+    going wrong?
+
+       Nothing.  This is just a diagnosis of a condition that had
+       not been diagnosed before.  If you are getting a lot of these
+       from a single host, there is probably some incompatibility
+       between 8.x and that host.  If you get a lot of them in
+       general, you may have network problems that are causing
+       connections to get reset.
+----------------------------------------------------------------------
+  * I just upgraded to 8.x and now when my users try to forward their
+    mail to a program they get an "illegal shell" message and their
+    mail is not delivered.  What's wrong?
+
+       In order for people to be able to run a program from their
+       .forward file, 8.x insists that their shell (that is, the
+       shell listed for that user in the passwd entry) be a "valid"
+       shell, meaning a shell listed in /etc/shells.  If /etc/shells
+       does not exist, a default list is used, typically consisting
+       of /bin/sh and /bin/csh.
+
+       This is to support environments that may have NFS-shared
+       directories mounted on machines on which users do not have
+       login permission.  For example, many people make their
+       file server inaccessible for performance or security
+       reasons; although users have directories, their shell on
+       the server is /usr/local/etc/nologin or some such.  If you
+       allowed them to run programs anyway you might as well let
+       them log in.
+
+       If you are willing to let users run programs from their
+       .forward file even though they cannot telnet or rsh in (as
+       might be reasonable if you run smrsh to control the list of
+       programs they can run) then add the line
+
+               /SENDMAIL/ANY/SHELL/
+
+       to /etc/shells.  This must be typed exactly as indicated,
+       in caps, with the trailing slash.  NOTA BENE:  DO NOT
+       list /usr/local/etc/nologin in /etc/shells -- this will
+       open up other security problems.
+----------------------------------------------------------------------
+  * I just upgraded to 8.x and suddenly connections to the SMTP port
+    take a long time.  What is going wrong?
+
+       It's probably something weird in your TCP implementation that
+       makes the IDENT code act oddly.  On most systems V8 tries to
+       do a ``callback'' to the connecting host to get a validated
+       user name (see RFC 1413 for detail).  If the connecting host
+       does not support such a service it will normally fail quickly
+       with "Connection refused", but certain kinds of packet
+       filters and certain TCP implementations just time out.
+
+       To test this, set the IDENT timeout to zero using
+       ``OrIdent=0'' in the configuration file.  This will
+       completely disable all use of the IDENT protocol.
+
+       Another possible problem is that you have your name server
+       and/or resolver configured improperly.  Make sure that all
+       "nameserver" entries in /etc/resolv.conf point to functional
+       servers.  If you are running your own server make certain
+       that all the servers listed in your root cache (usually
+       called something like "/var/namedb/root.cache"; see your
+       /etc/named.boot file to get your value) are up to date.
+       Either of these can cause long delays.
+----------------------------------------------------------------------
+  * I just upgraded to 8.x and suddenly I get errors such as ``unknown
+    mailer error 5 -- mail: options MUST PRECEDE recipients.''  What is
+    going wrong?
+
+       You need OSTYPE(systype) in your .mc file -- otherwise the
+       configurations use a default that probably disagrees with
+       your local mail system.  See cf/README for details.
+----------------------------------------------------------------------
+  * Under V8, the "From " header gets mysteriously munged when I send
+    to an alias.
+
+       ``It's not a bug, it's a feature.''  This happens when you
+       have a "owner-list" alias and you send to "list".  V8
+       propagates the owner information into the envelope sender
+       field (which appears as the "From " header on UNIX mail or as
+       the Return-Path: header) so that downstream errors are
+       properly returned to the mailing list owner instead of to the
+       sender.  In order to make this appear as sensible as possible
+       to end users, I recommend making the owner point to a
+       "request" address -- for example:
+
+               list:           :include:/path/name/list.list
+               owner-list:     list-request
+               list-request:   eric
+
+       This will make message sent to "list" come out as being "From
+       list-request" instead of "From eric".
+----------------------------------------------------------------------
+  * I am trying to use MASQUERADE_AS (or the user database) to
+    rewrite from addresses, and although it works in the From: header
+    line, it doesn't work in the envelope (e.g., the "From " line).
+
+       Believe it or not, this is intentional.  The interpretation
+       of the standards by the V8 development group was that this
+       was an inappropriate rewriting, and that if the rewriting
+       were incorrect at least the envelope would contain a valid
+       return address.  Other people have since described scenarios
+       where the envelope cannot be correct without this rewriting,
+       so 8.7 will have an option to rewrite both header and
+       envelope.
 ----------------------------------------------------------------------
   * I want to run Sendmail version 8 on my DEC system, but you don't
     have MAIL11V3 support in sendmail.  How do I handle this?
 
-       Get Paul Vixie's reimplementation of the mail11 protocol
-       from gatekeeper.dec.com in /pub/DEC/gwtools.
+       Get Paul Vixie's reimplementation of the mail11 protocol from
+       gatekeeper.dec.com in /pub/DEC/gwtools.
+
+       Rumour has it that he will be fully integrating into sendmail
+       V8 what little is left of IDA sendmail that is not handled
+       (or handled as well) by V8.  No additional information on
+       this project is currently available.
+----------------------------------------------------------------------
+  * Messages seem to disappear from my queue unsent.  When I look in
+    the queue directory I see that they have been renamed from qf* to
+    Qf*, and sendmail doesn't see these.
+
+       If you look closely you should find that the Qf files are
+       owned by users other than root.  Since sendmail runs as root
+       it refuses to believe information in non-root-owned qf files,
+       and it renames them to Qf to get them out of the way and make
+       it easy for you to find.  The usual cause of this is
+       twofold:  first, you have the queue directory world writable
+       (which is probably a mistake -- this opens up other security
+       problems) and someone is calling sendmail with an "unsafe"
+       flag, usually a -o flag that sets an option that could
+       compromise security.  When sendmail sees this it gives up
+       setuid root permissions.
+
+       The usual solution is to not use the problematic flags.  If
+       you must use them, you have to write a special queue
+       directory and have them processed by the same uid that
+       submitted the job in the first place.
 ----------------------------------------------------------------------
+@(#)FAQ        8.16    (Berkeley)      9/17/95
+Send updates to sendmail@sendmail.ORG.
index f34c6b7..f9020e7 100644 (file)
@@ -1,7 +1,7 @@
 
 
             K N O W N   B U G S   I N   S E N D M A I L
-                            (for 8.6.7)
+                            (for 8.7)
 
 
 The following are bugs or deficiencies in sendmail that I am aware of
@@ -14,10 +14,10 @@ distribution).
 This list is not guaranteed to be complete.
 
 
-* Null bytes are not handled properly.
+* Null bytes are not handled properly in headers.
 
   Sendmail should handle full binary data.  As it stands, it handles
-  any value from 0x01-0xFF in the body and 0x01-0x80 and 0xA0-0xFF in
+  all values in the body, but only 0x01-0x80 and 0xA0-0xFF in
   the header.  Notably missing is 0x00, which would require a major
   restructuring of the code -- for example, almost no C library support
   could be used to handle strings.
@@ -27,12 +27,6 @@ This list is not guaranteed to be complete.
   Sometimes identical, duplicate error messages can be generated.  As
   near as I can tell, this is rare and relatively innocuous.
 
-* No "exposed users" in "nullrelay" configuration.
-
-  The "nullrelay" configuration hides all addresses behind the mail
-  hub name.  Some sites might prefer to expose some names such as
-  root.  This information is always available in Received: lines.
-
 * $c (hop count) macro improperly set.
 
   The $c macro is supposed to contain the current hop count, for use
@@ -46,14 +40,6 @@ This list is not guaranteed to be complete.
   this address.  It's not clear what the right behaviour is in this
   circumstance.
 
-* REDIRECT aliases don't work with `n' option.
-
-  If you have option `n' set when you use newaliases and have
-  REDIRECT addresses in your aliases file, you'll get the error
-  messages during the newaliases instead of when email is sent to
-  the address in question.  The workaround is to turn off the `n'
-  option.
-
 * MX records that point at non-existent hosts work strangly.
 
   Consider the DNS records:
@@ -68,29 +54,11 @@ This list is not guaranteed to be complete.
   case is hostB, which is unknown.  It probably ought to eliminate
   hostB early in processing.
 
-* NAME environment variables with commas break.
-
-  If you define your NAME environment variable to have a comma
-  (e.g., ``Lastname, Firstname''), and you are using the $q definition
-  that uses ``name <address>'' format, sendmail treats the first and
-  last names as two addresses, thus producing a bogus From line.  You
-  can work around this by changing the $q definition to use
-  ``address (name)''.
-
 * \231 considered harmful.
 
   Header addresses that have the \231 character (and possibly others
   in the range \201 - \237) behave in odd and usually unexpected ways.
 
-* DEC Alphas (OSF/1 1.3) sometimes time out on sending mail.
-
-  I have one report that DEC Alphas acting as SMTP clients sometimes
-  will apparently not see the "250 OK" message in response to the
-  dot that indicates the end of the message.  This only happens if
-  the message is run from the queue -- if it gets through on first
-  try, everything is fine.  I have been unable to reproduce this
-  problem at Berkeley.
-
 * accept() problem on SVR4.
 
   Apparently, the sendmail daemon loop (doing accept()s on the network)
@@ -128,4 +96,24 @@ This list is not guaranteed to be complete.
   file descriptors per list).  This is particularly egregious if
   you have your connection cache set to be large.
 
-(Version 8.18, last updated 3/14/94)
+* Connection caching breaks if you pass the port number as an argument.
+
+  If you have a definition such as:
+
+         Mport,          P=[IPC], F=kmDFMuX, S=11/31, R=21,
+                         M=2100000, T=DNS/RFC822/SMTP,
+                         A=IPC [127.0.0.1] $h
+
+  (i.e., where $h is the port number instead of the host name) the
+  connection caching code will break because it won't notice that
+  two messages addressed to different ports should use different
+  connections.
+
+* ESMTP SIZE underestimates the size of a message
+
+  Sendmail makes no allowance for headers that it adds, nor does it
+  account for the SMTP on-the-wire \r\n expansion.  It probably doesn't
+  allow for 8->7 bit MIME conversions either.
+
+
+(Version 8.21, last updated 8/27/95)
index e1d97b6..a962423 100644 (file)
@@ -1,28 +1,8 @@
-#       @(#)Makefile   8.3 (Berkeley) 2/27/94
+#       @(#)Makefile   8.12 (Berkeley) 5/29/95
 
 SUBDIR= src mailstats makemap praliases cf/cf
 .if make(install)
 SUBDIR+= doc/intro doc/op
 .endif
 
-FTPDIR=        barad-dur:/disks/barad-dur/ftp/sendmail/.
-VER=   XX
-
-tar: Files.base Files.cf Files.misc Files.xdoc
-       (cd src; ${MAKE})
-       (cd doc; PRINTER=ps ${MAKE})
-       (cd doc; chmod 444 op/op.ps intro/intro.ps usenix/usenix.ps)
-       (cd cf/cf; ${MAKE})
-       pax -w -x tar -L -f sendmail.${VER}.base.tar `grep -v ^# Files.base`
-       compress sendmail.${VER}.base.tar
-       pax -w -x tar -L -f sendmail.${VER}.cf.tar `grep -v ^# Files.cf`
-       compress sendmail.${VER}.cf.tar
-       pax -w -x tar -L -f sendmail.${VER}.misc.tar `grep -v ^# Files.misc`
-       compress sendmail.${VER}.misc.tar
-       pax -w -x tar -L -f sendmail.${VER}.xdoc.tar `grep -v ^# Files.xdoc`
-       compress sendmail.${VER}.xdoc.tar
-
-ftp: sendmail.${VER}.base.tar.Z sendmail.${VER}.cf.tar.Z sendmail.${VER}.misc.tar.Z sendmail.${VER}.xdoc.tar.Z
-       rcp sendmail.${VER}.*.tar.Z RELEASE_NOTES FAQ KNOWNBUGS ${FTPDIR}
-
 .include <bsd.subdir.mk>
index 016fa7f..2e50d08 100644 (file)
@@ -1,16 +1,21 @@
 /*-
- *     @(#)READ_ME     8.10 (Berkeley) 4/13/94
+ *     @(#)READ_ME     8.25 (Berkeley) 11/19/95
  */
 
                        SENDMAIL RELEASE 8
 
 This directory has the latest sendmail software from Berkeley.  See
-doc/op/op.me for a summary of changes since 5.67.
+doc/changes/changes.me for a summary of changes since 5.67.
 
-Report any bugs to sendmail@CS.Berkeley.EDU.
+Report any bugs to sendmail-bugs@sendmail.ORG
 
-The latest version of sendmail is kept on FTP.CS.Berkeley.EDU, directory
-/ucb/sendmail; check there for the latest revision.
+******************************************************************
+**  DO NOT USE MAKE to compile sendmail.  Instead, cd src and  **
+**  use the "makesendmail" shell script.  On many environments **
+**  this will do everything for you, no fuss, no muss.  See    **
+**  src/READ_ME for more details of compilation.  See cf/README        **
+**  for details about building a runtime configuration file.   **
+******************************************************************
 
 
 +--------------+
@@ -28,7 +33,26 @@ The strip.sed file is only used in installation.
 After installation, edit tmac.doc and tmac.andoc to reflect the
 installation path of the tmac files.  Those files contain pointers to
 /usr/share/tmac/, and those pointers are not changed by the `make
-install` process.
+install` process.  There's also a bug in those files -- make the
+following patch:
+
+*** tmac.an~    Tue Jul 12 14:29:09 1994
+--- tmac.an     Fri Jul 15 13:17:54 1994
+***************
+*** 50,55 ****
+  .de TH
+  .rn TH xX
+  .so /usr/share/lib/tmac/tmac.an.old
+! .TH \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+  .rm xX
+  ..
+--- 50,55 ----
+  .de TH
+  .rn TH xX
+  .so /usr/share/lib/tmac/tmac.an.old
+! .TH "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8"
+  .rm xX
+  ..
 
 Rename the existing tmac.an to be tmac.an.old, and rename tmac.andoc
 to be tmac.an.
@@ -104,8 +128,13 @@ Important RFCs for electronic mail are:
        RFC976  UUCP mail format
        RFC1123 Host requirements (modifies 821, 822, and 974)
        RFC1413 Identification server
-       RFC1341 MIME: Multipurpose Internet Mail Extensions
+       RFC1425 SMTP Service Extensions (ESMTP spec)
+       RFC1426 SMTP Service Extension for 8bit-MIMEtransport
+       RFC1427 SMTP Service Extension for Message Size Declaration
+       RFC1521 MIME: Multipurpose Internet Mail Extensions
        RFC1344 Implications of MIME for Internet Mail Gateways
+       RFC1428 Transition of Internet Mail from Just-Send-8 to
+               8-bit SMTP/MIME
 
 Other standards that may be of interest (but which are less directly
 relevant to sendmail) are:
@@ -114,8 +143,8 @@ relevant to sendmail) are:
        RFC1049 Content-Type header field (extension to RFC822)
 
 Warning to AIX users: this version of sendmail does not implement
-MB, MR, or MG DNS resource records, as defined as experiments in
-RFC883.
+MB, MR, or MG DNS resource records, as defined (as experiments) in
+RFC1035.
 
 
 +-------------------+
@@ -138,32 +167,26 @@ about NEWDB and NDBM coexisting.
 
 
 +--------------------+
-| Host Name Services |
+| HOST NAME SERVICES |
 +--------------------+
 
-If you compile with NAMED_BIND (the default) sendmail will use
-DNS (the Domain Name System) for most host name lookups.  If
-you do not have DNS running at your site you may have to turn
-this off to cause sendmail to use NIS and/or the /etc/hosts file.
-In particular, on SunOS you have to choose to use DNS (which
-you should do if you are attached to the Internet, otherwise
-you lose MX records, which are required) or NIS -- there is no
-way to try both.
-
-If you are using NIS and /etc/hosts, it is critical that you
-list the long (fully qualified) name first in the /etc/hosts file
-used to build the NIS database.  For example, the line should read
+If you are using NIS or /etc/hosts, it is critical that you
+list the long (fully qualified) name somewhere (preferably first) in
+the /etc/hosts file used to build the NIS database.  For example, the
+line should read
 
        128.32.149.68   mastodon.CS.Berkeley.EDU mastodon
 
 **** NOT ****
 
-       128.32.149.68   mastodon mastodon.CS.Berkeley.EDU
+       128.32.149.68   mastodon
 
-If you use the wrong order, sendmail will conclude that your
-canonical name is the short version and use that in messages.
-The name "mastodon" doesn't mean much outside of Berkeley,
-and so this creates incorrect and unreplyable messages.
+If you do not include the long name, sendmail will complain loudly
+about ``unable to qualify my own domain name (mastodon) -- using
+short name'' and conclude that your canonical name is the short
+version and use that in messages.  The name "mastodon" doesn't mean
+much outside of Berkeley, and so this creates incorrect and unreplyable
+messages.
 
 
 +-------------+
@@ -178,6 +201,23 @@ with the old version of sendmail, so it's safe to go ahead and
 install it.
 
 
++----------------+
+| USE WITH IDENT |
++----------------+
+
+Sendmail 8 supports the IDENT protocol, as defined by RFC 1413.
+No ident server is included with this distribution.  I have found
+copies available on:
+
+  ftp.lysator.liu.se           /pub/ident/servers
+  romulus.ucs.uoknor.edu       /networking/ident/servers
+  ftp.cyf-kr.edu.pl            /agh/uciagh/network/ident
+
+If you want to run an IDENT server, I suggest getting a copy from
+one of those sites.  Versions are available for several different
+systems, including Apollo, BSD, NeXT, AIX, TOPS20, and VMS.
+
+
 +-----------+
 | MAKEFILES |
 +-----------+
@@ -219,6 +259,10 @@ contrib            Some contributed tools to help with sendmail.  THESE
                on the 4.4BSD tape.)
 doc            Documentation.  If you are getting source, read
                op.me -- it's long, but worth it.
+mail.local     The source for the local delivery agent used for 4.4BSD.
+               THIS IS NOT PART OF SENDMAIL! and may not compile
+               everywhere, since it depends on some 4.4-isms.  Warning:
+               it does mailbox locking differently than other systems.
 mailstats      Statistics printing program.  It has the pathname of
                sendmail.st compiled in, so if you've changed that,
                beware.  This isn't all that useful.
@@ -228,12 +272,17 @@ makemap           A program that creates the keyed maps used by the $( ... $)
                expect to preprocess must human-convenient formats
                using sed scripts before this program will like them.
                But it should be functionally complete.
-praliases      A program to print the DBM version of the aliases file.
-               It hasn't been converted to understand the new Berkeley
-               DB format (which we are using).
+praliases      A program to print the DBM or NEWDB version of the
+               aliases file.
 rmail          Source for rmail(8).  This is used as a delivery
                agent for for UUCP, and could presumably be used by
                other non-socket oriented mailers.  Older versions of
-               rmail are probably deficient.
+               rmail are probably deficient.  RMAIL IS NOT PART OF
+               SENDMAIL!!!  The 4.4BSD source is included for you to
+               look at or try to port to your system.  I know it doesn't
+               compile on {SunOS, HP-UX, OSF/1, other} (pick one).
+smrsh          The "sendmail restricted shell", which can be used as
+               a replacement for /bin/sh in the prog mailer to provide
+               increased security control.  NOT PART OF SENDMAIL!
 src            Source for the sendmail program itself.
 test           Some test scripts (currently only for compilation aids).
index 98f7461..7354490 100644 (file)
                        SENDMAIL RELEASE NOTES
-            @(#)RELEASE_NOTES  8.6.12.1 (Berkeley) 3/28/95
+            @(#)RELEASE_NOTES  8.7.3.1 (Berkeley) 12/3/95
 
 This listing shows the version of the sendmail binary, the version
 of the sendmail configuration files, the date of release, and a
 summary of the changes in that release.
 
+8.7.3/8.7.3    95/12/xx
+       Fix botch in name server timeout in RCPT code; this problem caused
+               two responses in SMTP, which breaks things horribly.  Fix
+               from Gregory Neil Shapiro of WPI.
+       Verify that L= value on M lines cannot be negative, which could cause
+               negative array subscripting.  Not a security problem since
+               this has to be in the config file, but it could have caused
+               core dumps.  Pointed out by Bryan Costales.
+       Fix -d21 debug output for long macro names.  Pointed out by Bryan
+               Costales.
+       PORTABILITY FIXES:
+               SCO doesn't have ftruncate.  From Bill Aten of Computerizers.
+               IBM's version of arpa/nameser.h defaults to the wrong byte
+                       order.  Tweak it to work properly.  Based on fixes
+                       from Fletcher Mattox of UTexas and Betty Lee of
+                       Stanford University.
+       CONFIG: add confHOSTS_FILE m4 variable to set HostsFile option.
+               Deficiency pointed out by Bryan Costales of ICSI.
+
+8.7.2/8.7.2    95/11/19
+       REALLY fix the backslash escapes in SmtpGreetingMessage,
+               OperatorChars, and UnixFromLine options.  They were not
+               properly repaired in 8.7.1.
+       Completely delete the Bcc: header if and only if there are other
+               valid recipient headers (To:, Cc: or Apparently-To:, the
+               last being a historic botch, of course).  If Bcc: is the
+               only recipient header in the message, it's value is tossed,
+               but the header name is kept.  The old behaviour (always keep
+               the header name and toss the value) allowed primary recipients
+               to see that a Bcc: went to _someone_.
+       Include queue id on ``Authentication-Warning: <host>: <user> set
+               sender to <addresss> using -f'' syslog messages.  Suggested
+               by Kari Hurtta.
+       If a sequence or switch map lookup entry gets a tempfail but then
+               continues on to another map type, but the name is not found,
+               return a temporary failure from the sequence or switch map.
+               For example, if hosts search ``dns files'' and DNS fails
+               with a tempfail, the hosts map will go on and search files,
+               but if it fails the whole thing should be a tempfail, not
+               a permanent (host unknown) failure, even though that is the
+               failure in the hosts.files map.  This error caused hard
+               bounces when it should have requeued.
+       Aliases to files such as /users/bar/foo/inbox, with /users/bar/foo
+               owned by bar mode 700 and inbox being setuid bar stopped
+               working properly due to excessive paranoia.  Pointed out by
+               John Hawkinson of Panix.
+       An SMTP RCPT command referencing a host that gave a nameserver
+               timeout would return a 451 command (8.6 accepted it and
+               queued it locally).  Revert to the 8.6 behaviour in order
+               to simplify queue management for clustered systems.  Suggested
+               by Gregory Neil Shapiro of WPI.  The same problem could break
+               MH, which assumes that the SMTP session will succeed (tsk, tsk
+               -- mail gets lost!); this was pointe dout by Stuart Pook of
+               Infobiogen.
+       Fix possible buffer overflow in munchstring().  This was not a security
+               problem because you couldn't specify any argument to this
+               without first giving up root privileges, but it is still a
+               good idea to avoid future problems.  Problem noted by John
+               Hawkinson and Sam Hartman of MIT.
+       ``452 Out of disk space for temp file'' messages weren't being
+               printed.  Fix from David Perlin of Nanosoft.
+       Don't advertise the ESMTP DSN extension if the SendMIMEErrors option
+               is not set, since this is required to get the actual DSNs
+               created.  Problem pointed out by John Gardiner Myers of CMU.
+       Log permission problems that cause .forward and :include: files to
+               be untrusted or ignored on log level 12 and higher.  Suggestted
+               by Randy Martin of Clemson University.
+       Allow user ids in U= clauses of M lines to have hyphens and
+               underscores.
+       Fix overcounting of recipients -- only happened when sending to an
+               alias.  Pointed out by Mark Andrews of SGI and Jack Woolley
+               of Systems and Computer Technology Corporation.
+       If a message is sent to an address that fails, the error message that
+               is returned could show some extraneous "success" information
+               included even if the user did not request success notification,
+               which was confusing.  Pointed out by Allan Johannesen of WPI.
+       Config files that had no AliasFile definition were defaulting to
+               using /etc/aliases; this caused problems with nullclient
+               configurations.  Change it back to the 8.6 semantics of
+               having no local alias file unless it is declared.  Problem
+               noted by Charles Karney of Princeton University.
+       Fix compile problem if NOTUNIX is defined.  Pointed out by Bryan
+               Costales of ICSI.
+       Map lookups of class "userdb" maps were always case sensitive; they
+               should be controlled by the -f flag like other maps.  Pointed
+               out by Bjart Kvarme <bjart.kvarme@usit.uio.no>.
+       Fix problem that caused some addresses to be passed through ruleset 5
+               even when they were tagged as "sticky" by prefixing the
+               address with an "@".  Patch from Thomas Dwyer III of Michigan
+               Technological University.
+       When converting a message to Quoted-Printable, prevent any lines with
+               dots alone on a line by themselves.  This is because of the
+               preponderence of broken mailers that still get this wrong.
+               Code contributed by Per Hedeland of Ericsson.
+       Fix F{macro}/file construct -- it previously did nothing.  Pointed
+               out by Bjart Kvarme of USIT/UiO (Norway).
+       Announce whether a cached connection is SMTP or ESMTP (in -v mode).
+               Requested by Allan Johannesen.
+       Delete check for text format of alias files -- it should be legal
+               to have the database format of the alias files without the
+               text version.  Problem pointed out by Joe Rhett of Navigist,
+               Inc.
+       If "Ot" was specified with no value, the TZ variable was not properly
+               imported from the environment.  Pointed out by Frank Crawford
+               <frank@ansto.gov.au>.
+       Some architectures core dumped on "program" maps that didn't have
+               extra arguments.  Patch from Booker C. Bense of Stanford
+               University.
+       Queue run processes would re-spawn daemons when given a SIGHUP; only
+               the parent should do this.  Fix from Brian Coan of the
+               Association for Progressive Communications.
+       If MinQueueAge was set and a message was considered but not run
+               during a queue run and the Timeout.queuereturn interval was
+               reached, a "timed out" error message would be returned that
+               didn't include the failed address (and claimed to be a warning
+               even though it was fatal).  The fix is to not return such
+               messages until they are actually tried, i.e., in the next
+               MinQueueAge interval.  Problem noted by Rein Tollevik of
+               SINTEF RUNIT, Oslo.
+       Add HES_GETMAILHOST compile flag to support MIT Hesiod distributions
+               that have the hes_getmailhost() routine.  DEC Hesiod
+               distributions do not have this routine.  Based on a patch
+               from Betty Lee of Stanford University.
+       Extensive cleanups to map open code to handle a locking race condition
+               in ndbm, hash, and btree format database files on some (most
+               non-4.4-BSD based) OS architectures.  This should solve the
+               occassional "user unknown" problem during alias rebuilds that
+               has plagued me for quite some time.  Based on a patch from
+               Thomas Dwyer III of Michigan Technological University.
+       PORTABILITY FIXES:
+               Solaris: Change location of newaliases and mailq from
+                       /usr/ucb to /usr/bin to match Sun settings.  From
+                       James B. Davis of TCI.
+               DomainOS: Makefile.DomainOS doesn't require -ldbm.  From
+                       Don Lewis of Silicon Systems.
+               HP-UX 10: rename Makefile.HP-UX.10 => Makefile.HP-UX.10.x
+                       so that the makesendmail script will find it.  Pointed
+                       out by Richard Allen of the University of Iceland.
+                       Also, use -Aa -D_HPUX_SOURCE instead of -Ae, which
+                       isn't supported on all compilers.
+               UXPDS: compilation fixes from Diego R. Lopez.
+       CONFIG: FAX mailer wasn't setting .FAX as a pseudo-domain unless
+               you also had a FAX_RELAY.  From Thomas.Tornblom@Hax.SE.
+       CONFIG: Minor glitch in S21 -- attachment of local domain name
+               didn't have trailing dot.  From Jim Hickstein of Teradyne.
+       CONFIG: Fix best_mx_is_local feature to allow nested addresses such as
+               user%host@thishost.  From Claude Scarpelli of Infobiogen
+               (France).
+       CONFIG: OSTYPE(hpux10) failed to define the location of the help file.
+               Pointed out by Hannu Martikka of Nokia Telecommunications.
+       CONFIG: Diagnose some inappropriate ordering in configuration files,
+               such as FEATURE(smrsh) listed after MAILER(local).  Based on
+               a bug report submitted by Paul Hoffman of Proper Publishing.
+       CONFIG: Make OSTYPE files consistently not override settings that
+               have already been set.  Previously it worked differently
+               for different files.
+       CONFIG: Change relay mailer to do masquerading like 8.6 did.  My take
+               is that this is wrong, but the change was causing problems
+               for some people.  From Per Hedeland of Ericsson.
+       CONTRIB: bitdomain.c patch from John Gardiner Myers <jgm+@CMU.EDU>;
+               portability changes for Posix environments (no functional
+               changes).
+
+8.7.1/8.7.1    95/10/01
+       Old macros that have become options (SmtpGreetingMessage,
+               OperatorChars, and UnixFromLine) didn't allow backslash
+               escapes in the options, where they previously had.  Bug
+               pointed out by John Hawkinson of MIT.
+       Fix strange case of an executable called by a program map that
+               returns a value but also a non-zero exit status; this
+               would give contradictory results in the higher level; in
+               particular, the default clause in the map lookup would be
+               ignored.  Change to ignore the value if the program returns
+               non-zero exit status.  From Tom Moore of AT&T GIS.
+       Shorten parameters passed to syslog() in some contexts to avoid a
+               bug in many vendors' implementations of that routine.  Although
+               this isn't really a bug in sendmail per se, and my solution
+               has to assume that syslog() has at least a 1K buffer size
+               internally (I know some vendors have shortened this
+               dramatically -- they're on their own), sendmail is a popular
+               target.  Also, limit the size of %s arguments in sprintf.
+               These both have possible security implications.  Solutions
+               suggested by Casper Dik of Sun's Network Security Group
+               (Holland), Mark Seiden, and others.
+       Fix a problem that might cause a non-standard -B (body type)
+               parameter to be passed to the next server with undefined
+               results.  This could have security implications.
+       If a filesystem was at > 100% utilization, the freediskspace()
+               routine incorrectly returned an error rather than zero.
+               Problem noted by G. Paul Ziemba of Alantec.
+       Change MX sort order so that local hostnames (those in $=w) always
+               sort first within a given preference.  This forces the bestmx
+               map to always return the local host first, if it is included
+               in the list of highest priority MX records.  From K. Robert
+               Elz.
+       Avoid some possible null pointer dereferences.  Fixes from Randy
+               Martin <WOLF@CLEMSON.EDU>
+       When sendmail starts up on systems that have no fully qualified
+               domain name (FQDN) anywhere in the first matching host map
+               (e.g., /etc/hosts if the hosts service searches "files dns"),
+               sendmail would sleep to try to find a FQDN, which it really
+               really needs.  This has been changed to fall through to the
+               next map type if it can't find a FQDN -- i.e., if the hosts
+               file doesn't have a FQDN, it will try dns even though the
+               short name was found in /etc/hosts.  This is probably a crock,
+               but many people have hosts files without FQDNs.  Remember:
+               domain names are your friends.
+       Log a high-priority message if you can't find your FQDN during startup.
+               Suggested by Simon Barnes of Schlumberger Limited.
+       When using Hesiod, initialize it early to improve error reporting.
+               Patch from Don Lewis of Silicon Systems, Inc.
+       Apparently at least some versions of Linux have a 90 !minute! TCP
+               connection timeout in the kernel.  Add a new "connect" timeout
+               to limit this time.  Defaults to zero (use whatever the
+               kernel provides).  Based on code contributed by J.R. Oldroyd
+               of TerraNet.
+       Under some circumstances, a failed message would not be properly
+               removed from the queue, causing tons of bogus error messages.
+               (This fix eliminates the problematic EF_KEEPQUEUE flag.)
+               Problem noted by Allan E Johannesen and Gregory Neil Shapiro
+               of WPI.
+       PORTABILITY FIXES:
+               On IRIX 5.x, there was an inconsistency in the setting
+                       of sendmail.st location.  Change the Makefile to
+                       install it in /var/sendmail.st to match the OSTYPE
+                       file and SGI standards.  From Andre
+                       <andre@curry.zfe.siemens.de>.
+               Support for Fujitsu/ICL UXP/DS (For the DS/90 Series)
+                       from Diego R. Lopez <drlopez@cica.es>.
+               Linux compilation patches from J.R. Oldroyd of TerraNet, Inc.
+               LUNA 2 Mach patches from Motonori Nakamura.
+               SunOS Makefile was including -ldbm, which is for the old
+                       dbm library.  The ndbm library is part of libc.
+       CONFIG: avoid bouncing ``user@host.'' (note trailing dot) with
+               ``local configuration error'' in nullclient configuration.
+               Patch from Gregory Neil Shapiro of WPI.
+       CONFIG: don't allow an alias file in nullclient configurations --
+               since all addresses are relayed, they give errors during
+               rebuild.  Suggested by Per Hedeland of Ericsson.
+       CONFIG: local mailer on Solaris 2 should always get a -f flag because
+               otherwise the F=S causes the From_ line to imply that root is
+               the sender.  Problem pointed out by Claude Scarpelli of
+               Infobiogen (France).
+       NEW FILES:
+               cf/feature/use_ct_file.m4 (omitted from 8.7 by mistake)
+               src/Makefiles/Makefile.KSR (omitted from 8.7 by mistake)
+               src/Makefiles/Makefile.UXPDS
+
+8.7/8.7                95/09/16
+       Fix a problem that could cause sendmail to run out of file
+               descriptors due to a trashed data structure after a
+               vfork.  Fix from Brian Coan of the Institute for
+               Global Communications.
+       Change the VRFY response if you have disabled VRFY -- some
+               people seemed to think that it was too rude.
+       Avoid reference to uninitialized file descriptor if HASFLOCK
+               was not defined.  This was used "safely" in the sense
+               that it only did a stat, but it would have set the
+               map modification time improperly.  Problem pointed out
+               by Roy Mongiovi of Georgia Tech.
+       Clean up the Subject: line on warning messages and return
+               receipts so that they don't say "Returned mail:"; this
+               can be confusing.
+       Move ruleset entry/exit debugging from 21.2 to 21.1 -- this is
+               useful enough to make it worthwhile printing on "-d".
+       Avoid logging alias statistics every time you read the alias
+               file on systems with no database method compiled in.
+       If you have a name with a trailing dot, and you try looking it
+               up using gethostbyname without the dot (for /etc/hosts
+               compatibility), be sure to turn off RES_DEFNAMES and
+               RES_DNSRCH to avoid finding the wrong name accidently.
+               Problem noted by Charles Amos of the University of
+               Maryland.
+       Don't do timeouts in collect if you are not running SMTP.
+               There is nothing that says you can't have a long
+               running program piped into sendmail (possibly via
+               /bin/mail, which just execs sendmail).  Problem reported
+               by Don "Truck" Lewis of Silicon Systems.
+       Try gethostbyname() even if the DNS lookup fails iff option I
+               is not set.  This allows you to have hosts listed in
+               NIS or /etc/hosts that are not known to DNS.  It's normally
+               a bad idea, but can be useful on firewall machines.  This
+               should really be broken out on a separate flag, I suppose.
+       Avoid compile warnings against BIND 4.9.3, which uses function
+               prototypes.  From Don Lewis of Silicon Systems.
+       Avoid possible incorrect diagnosis of DNS-related errors caused
+               by things like attempts to resolve uucp names using
+               $[ ... $] -- the fix is to clear h_errno at appropriate
+               times.  From Kyle Jones of UUNET.
+       SECURITY: avoid denial-of-service attacks possible by destroying
+               the alias database file by setting resource limits low.
+               This involves adding two new compile-time options:
+               HASSETRLIMIT (indicating that setrlimit(2) support is
+               available) and HASULIMIT (indicating that ulimit(2) support
+               is available -- the Release 3 form is used).  The former
+               is assumed on BSD-based systems, the latter on System
+               V-based systems.  Attack noted by Phil Brandenberger of
+               Swarthmore University.
+       New syntaxes in test (-bt) mode:
+               ``.Dmvalue'' will define macro "m" to "value".
+               ``.Ccvalue'' will add "value" to class "c".
+               ``=Sruleset'' will dump the contents of the indicated
+                       ruleset.
+               ``=M'' will display the known mailers.
+               ``-ddebug-spec'' is equivalent to the command-line
+                       -d debug flag.
+               ``$m'' will print the value of macro $m.
+               ``$=c'' will print the contents of class $=c.
+               ``/mx host'' returns the MX records for ``host''.
+               ``/parse address'' will parse address, returning the value of
+                       crackaddr (essentially, the comment information)
+                       and the parsed address (the same as -bv).
+               ``/try mailer address'' will rewrite address into the form
+                       it will have when presented to the indicated mailer.
+               ``/tryflags flags'' will set flags used by parsing.  The
+                       flags can be `H' for header or `E' for envelope,
+                       and `S' for sender or `R' for recipient.  These
+                       can be combined, so `HR' sets flags for header
+                       recipients.
+               ``/canon hostname'' will try to canonify hostname and
+                       return the result.
+               ``/map mapname key'' will look up `key' in the indicated
+                       `mapname' and return the result.
+       Somewhat better handling of UNIX-domain socket addresses -- it
+               should show the pathname rather than hex bytes.
+       Restore ``-ba'' mode -- this reads a file from stdin and parses
+               the header for envelope sender information and uses
+               CR-LF as message terminators.  It was thought to be
+               obsolete (used only for Arpanet NCP protocols), but it
+               turns out that the UK ``Grey Book'' protocols require
+               that functionality.
+       Fix a fix in previous release -- if gethostname and gethostbyname
+               return a name without dots, and if an attempt to canonify
+               that name fails, wait one minute and try again.  This can
+               result in an extra 60 second delay on startup if your system
+               hostname (as returned by hostname(1)) has no dot and no names
+               listed in /etc/hosts or your NIS map have a dot.
+       Check for proper domain name on HELO and EHLO commands per
+               RFC 1123 section 5.2.5.  Problem noted by Thomas Dwyer III
+               of Michigan Technological University.
+       Relax chownsafe rules slightly -- old version said that if you
+               can't tell if _POSIX_CHOWN_RESTRICTED is set (that is,
+               if fpathconf returned EINVAL or ENOSYS), assume that
+               chown is not safe.  The new version falls back to whether
+               you are on a BSD system or not.  This is important for
+               SunOS, which apparently always returns one of those
+               error codes.  This impacts whether you can mail to files
+               or not.
+       Syntax errors such as unbalanced parentheses in the configuration
+               file could be omitted if you had "Oem" prior to the
+               syntax error in the config file.  Change to always print
+               the error message.  It was especially wierd because it
+               would cause a "warning" message to be sent to the Postmaster
+               for every message sent (but with no transcript).  Problem
+               noted by Gregory Paris of Motorola.
+       Rewrite collect and putbody to handle full 8-bit data, including
+               zero bytes.  These changes are internally extensive, but
+               should have minimal impact on external function.
+       Allow full words for option names -- if the option letter is
+               (apparently) a space, then take the word following -- e.g.,
+                       O MatchGECOS=TRUE
+               The full list of old and new names is as follows:
+                       7       SevenBitInput
+                       8       EightBitMode
+                       A       AliasFile
+                       a       AliasWait
+                       B       BlankSub
+                       b       MinFreeBlocks/MaxMessageSize
+                       C       CheckpointInterval
+                       c       HoldExpensive
+                       D       AutoRebuildAliases
+                       d       DeliveryMode
+                       E       ErrorHeader
+                       e       ErrorMode
+                       f       SaveFromLine
+                       F       TempFileMode
+                       G       MatchGECOS
+                       H       HelpFile
+                       h       MaxHopCount
+                       i       IgnoreDots
+                       I       ResolverOptions
+                       J       ForwardPath
+                       j       SendMimeErrors
+                       k       ConnectionCacheSize
+                       K       ConnectionCacheTimeout
+                       L       LogLevel
+                       l       UseErrorsTo
+                       m       MeToo
+                       n       CheckAliases
+                       O       DaemonPortOptions
+                       o       OldStyleHeaders
+                       P       PostmasterCopy
+                       p       PrivacyOptions
+                       Q       QueueDirectory
+                       q       QueueFactor
+                       R       DontPruneRoutes
+                       r, T    Timeout
+                       S       StatusFile
+                       s       SuperSafe
+                       t       TimeZoneSpec
+                       u       DefaultUser
+                       U       UserDatabaseSpec
+                       V       FallbackMXhost
+                       v       Verbose
+                       w       TryNullMXList
+                       x       QueueLA
+                       X       RefuseLA
+                       Y       ForkEachJob
+                       y       RecipientFactor
+                       z       ClassFactor
+                       Z       RetryFactor
+               The old macros that passed information into sendmail have
+               been changed to options; those correspondences are:
+                       $e      SmtpGreetingMessage
+                       $l      UnixFromLine
+                       $o      OperatorChars
+                       $q      (deleted -- not necessary)
+               To avoid possible problems with an older sendmail,
+               configuration level 6 is accepted by this version of
+               sendmail; any config file using the new names should
+               specify "V6" in the configuration.
+       Change address parsing to properly note that a phrase before a
+               colon and a trailing semicolon are essentially the same
+               as text outside of angle brackets (i.e., sendmail should
+               treat them as comments).  This is to handle the
+               ``group name: addr1, addr2, ..., addrN;'' syntax (it will
+               assume that ``group name:'' is a comment on the first
+               address and the ``;'' is a comment on the last address).
+               This requires config file support to get right.  It does
+               understand that :: is NOT this syntax, and can be turned
+               off completely by setting the ColonOkInAddresses option.
+       Level 6 config files added with new mailer flags:
+                   A   Addresses are aliasable.
+                   i   Do udb rewriting on envelope as well as header
+                       sender lines.  Applies to the from address mailer
+                       flags rather than the recipient mailer flags.
+                   j   Do udb rewriting on header recipient addresses.
+                       Applies to the sender mailer flags rather than the
+                       recipient mailer flags.
+                   k   Disable check for loops when doing HELO command.
+                   o   Always run as the mail recipient, even on local
+                       delivery.
+                   w   Check for an /etc/passwd entry for this user.
+                   5   Pass addresses through ruleset 5.
+                   :   Check for :include: on this address.
+                   |   Check for |program on this address.
+                   /   Check for /file on this address.
+                   @   Look up sender header addresses in the user
+                       database.  Applies to the mailer flags for the
+                       mailer corresponding to the envelope sender
+                       address, rather than to recipient mailer flags.
+               Pre-level 6 configuration files set A, w, 5, :, |, /, and @
+               on the "local" mailer, the o flag on the "prog" and "*file*"
+               mailers, and the ColonOkInAddresses option.
+       Eight-to-seven bit MIME conversions.  This borrows ideas from
+               John Beck of Hewlett-Packard, who generously contributed
+               their implementation to me, which I then didn't use (see
+               mime.c for an explanation of why).  This adds the
+               EightBitMode option (a.k.a. `8') and an F=8 mailer flag
+               to control handling of 8-bit data.  These have to cope with
+               two types of 8-bit data: unlabelled 8-bit data (that is,
+               8-bit data that is entered without declaring it as 8-bit
+               MIME -- technically this is illegal according to the
+               specs) and labelled 8-bit data (that is, it was declared
+               as 8BITMIME in the ESMTP session or by using the
+               -B8BITMIME command line flag).  If the F=8 mailer flag is
+               set then 8-bit data is sent to non-8BITMIME machines
+               instead of converting to 7 bit (essentially using
+               just-send-8 semantics).  The values for EightBitMode are:
+                   m   convert unlabelled 8-bit input to 8BITMIME, and do
+                       any necessary conversion of 8BITMIME to 7BIT
+                       (essentially, the full MIME option).
+                   p   pass unlabelled 8-bit input, but convert labelled
+                       8BITMIME input to 7BIT as required (default).
+                   s   strict adherence: reject unlabelled 8-bit input,
+                       convert 8BITMIME to 7BIT as required.  The F=8
+                       flag is ignored.
+               Unlabelled 8-bit data is rejected in mode `s' regardless of
+                       the setting of F=8.
+       Add new internal class 'n', which is the set of MIME Content-Types
+               which can not be 8 to 7 bit encoded because of other
+               considerations.  Types "multipart/*" and "message/*" are
+               never directly encoded (although their components can be).
+       Add new internal class 's', which is the set of subtypes of the
+               MIME message/* content type that can be treated as though
+               they are an RFC822 message.  It is predefined to have
+               "rfc822".  Suggested By Kari Hurtta.
+       Add new internal class 'e'.  This is the set of MIME
+               Content-Transfer-Encodings that can be converted to
+               a seven bit format (Quoted-Printable or Base64).  It is
+               preinitialized to contain "7bit", "8bit", and "binary".
+       Add C=charset mailer parameter and the the DefaultCharSet option (no
+               short name) to set the default character set to use in the
+               Content-Type: header when doing encoding of an 8-bit message
+               which isn't marked as MIME into MIME format.  If the C=
+               parameter is set on the Envelope From address, use that as
+               the default encoding; else use the DefaultCharSet option.
+               If neither is set, it defaults to "unknown-8bit" as
+               suggested by RFC 1428 section 3.
+       Allow ``U=user:group'' field in mailer definition to set a default
+               user and group that a mailer will be executed as.  This
+               overrides the 'u' and 'g' options, and if the `F=S' flag is
+               also set, it is the uid/gid that will always be used (that
+               is, the controlling address is ignored).  The values may be
+               numeric or symbolic; if only a symbolic user is given (no
+               group) that user's default group in the passwd file is used
+               as the group.  Based on code donated by Chip Rosenthal of
+               Unicom.
+       Allow `u' option to also accept user:group as a value, in the same
+               fashion as the U= mailer option.
+       Add the symbolic time zone name in the Arpanet format dates (as
+               a comment).  This adds a new compile-time configuration
+               flag: TZ_TYPE can be set to TZ_TM_NAME (use the value
+               of (struct tm *)->tm_name), TZ_TM_ZONE (use the value
+               of (struct tm *)->tm_zone), TZ_TZNAME (use extern char
+               *tzname[(struct tm *)->tm_isdst]), TZ_TIMEZONE (use
+               timezone()), or TZ_NONE (don't include the comment).  Code
+               from Chip Rosenthal.
+       The "Timeout" option (formerly "r") is extended to allow suboptions.
+               For example,
+                   O Timeout.helo = 2m
+               There are also two new suboptions "queuereturn" and
+               "queuewarn"; these subsume the old T option.  Thus, to
+               set them both the preferred new syntax is
+                   O Timeout.queuereturn = 5d
+                   O Timeout.queuewarn = 4h
+       Sort queue by host name instead of by message priority if the
+               QueueSortOrder option (no short name) is set is set to
+               ``host''.  This makes better use of the connection cache,
+               but may delay more ``interactive'' messages behind large
+               backlogs under some circumstances.  This is probably a
+               good option if you have high speed links or don't do lots
+               of ``batch'' messages, but less good if you are using
+               something like PPP on a 14.4 modem.  Based on code
+               contributed by Roy Mongiovi of Georgia Tech (my main
+               contribution was to make it configurable).
+       Save i-number of df file in qf file to simplify rebuilding of queue
+               after disasterous disk crash.  Suggested by Kyle Jones of
+               UUNET; closely based on code from KJS DECWRL code written
+               by Paul Vixie.  NOTA BENE: The qf files produced by 8.7
+               are NOT back compatible with 8.6 -- that is, you can convert
+               from 8.6 to 8.7, but not the other direction.
+       Add ``F=d'' mailer flag to disable all use of angle brackets in
+               route-addrs in envelopes; this is because in some cases
+               they can be sent to the shell, which interprets them as
+               I/O redirection.
+       Don't include error file (option E) with return-receipts; this
+               can be confusing.
+       Don't send "Warning: cannot send" messages to owner-* or
+               *-request addresses.  Suggested by Christophe Wolfhugel
+               of the Institut Pasteur, Paris.
+       Allow -O command line flag to set long form options.
+       Add "MinQueueAge" option to set the minimum time between attempts
+               to run the queue.  For example, if the queue interval
+               (-q value) is five minutes, but the minimum queue age
+               is fifteen minutes, jobs won't be tried more often than
+               once every fifteen minutes.  This can be used to give
+               you more responsiveness if your delivery mode is set to
+               queue-only.
+       Allow "fileopen" timeout (default: 60 seconds) for opening
+               :include: and .forward files.
+       Add "-k", "-v", and "-z" flags to map definitions; these set the
+               key field name, the value field name, and the field
+               delimiter.  The field delimiter can be a single character
+               or the sequence "\t" or "\n" for tab or newline.
+               These are for use by NIS+ and similar access methods.
+       Change maps to always strip quotes before lookups; the -q flag
+               turns off this behaviour.  Suggested by Motonori Nakamura.
+       Add "nisplus" map class.  Takes -k and -v flags to choose the
+               key and value field names respectively.  Code donated by
+               Sun Microsystems.
+       Add "hesiod" map class.  The "file name" is used as the
+               "HesiodNameType" parameter to hes_resolve(3).  Returns the
+               first value found for the match.  Code donated by Scott
+               Hutton of Indiana University.
+       Add "netinfo" (NeXT NetInfo) map class.  Maps can have a -k flag to
+               specify the name of the property that is searched as the
+               key and a -v flag to specify the name of the property that
+               is returned as the value (defaults to "members").  The
+               default map is "/aliases".  Some code based on code
+               contributed by Robert La Ferla of Hot Software.
+       Add "text" map class.  This does slow, linear searches through
+               text files.  The -z flag specifies a column delimiter
+               (defaults to any sequence of white space), the -k flag
+               sets the key column number, and the -v flag sets the
+               value column number.  Lines beginning with `#' are treated
+               as comments.
+       Add "program" map class to execute arbitrary programs.  The search
+               key is presented as the last argument; the output is one
+               line read from the programs standard output.  Exit statuses
+               are from sysexits.h.
+       Add "sequence" map class -- searches maps in sequence until it
+               finds a match.  For example, the declarations:
+                   Kmap1 ...
+                   Kmap2 ...
+                   Kmapseq sequence map1 map2
+               defines a map "mapseq" that first searches map1; if the
+               value is found it is returned immediately, otherwise
+               map2 is searched and the value returned.
+       Add "switch" map class.  This is much like "sequence" except that
+               the ordering is fetched from an external file, usually
+               the system service switch.  The parameter is the name of
+               the service to switch on, and the maps that it will use
+               are the name of the switch map followed by ".service_type".
+               For example, if the declaration of the map is
+                   Ksample switch hosts
+               and the system service switch specifies that hosts are
+               looked up using dns and nis in that order, then this is
+               equivalent to
+                   Ksample sequence sample.dns sample.nis
+               The subordinate maps (sample.*) must already be defined.
+       Add "user" map class -- looks up users using getpwnam.  Takes a
+               "-v field" flag on the definition that tells what passwd
+               entry to return -- legal values are name, passwd, uid, gid,
+               gecos, dir, and shell.  Generally expected to be used with
+               the -m (matchonly) flag.
+       Add "bestmx" map class -- returns the best MX value for the host
+               listed as the value.  If there are several "best" MX records
+               for this host, one will be chosen at random.
+       Add "userdb" map class -- looks up entries in the user database.
+               The "file name" is actually the tag that will be used,
+               typically "mailname".  If there are multiple entries
+               matching the name, the one chosen is undefined.
+       Add multiple queue timeouts (both return and warning).  These are
+               set by the Precedence: or Priority: header fields to one of
+               three values.  If a Priority: is set and has value "normal",
+               "urgent", or "non-urgent" the corresponding timeouts are 
+               used.  If no priority is set, the Precedence: is consulted;
+               if negative, non-urgent timeouts are used; if greater than
+               zero, urgent timeouts are used.  Otherwise, normal timeouts
+               are used.  The timeouts are set by setting the six timeouts
+               queue{warn,return}.{urgent,normal,non-urgent}.
+       Fix problem when a mail address is resolved to a $#error mailer
+               with a temporary failure indication; it works in SMTP,
+               but when delivering locally the mail is silently discarded.
+               This patch, from Kyle Jones of UUNET, bounces it instead
+               of queueing it (queueing is very hard).
+       When using /etc/hosts or NIS-style lookups, don't assume that
+               the first name in the list is the best one -- instead,
+               search for the first one with a dot.  For example, if
+               an /etc/hosts entry reads
+                   128.32.149.68       mammoth mammoth.CS.Berkeley.EDU
+               this change will use the second name as the canonical
+               machine name instead of the initial, unqualified name.
+       Change dequote map to replace spaces in quoted text with a value
+               indicated by the -s flag on the dequote map definition.
+               For example, ``Mdequote dequote -s_'' will change
+               "Foo Bar" into an unquoted Foo_Bar instead of leaving it
+               quoted (because of the space character).  Suggested by Dan
+               Oscarsson for use in X.400 addresses.
+       Implement long macro names as ${name}; long class names can
+               be similarly referenced as $={name} and $~{name}.
+               Definitions are (e.g.) ``D{name}value''.  Names that have
+               a leading lower case letter or punctuation characters are
+               reserved for internal use by sendmail; i.e., config files
+               should use names that begin with a capital letter.  Based
+               on code contributed by Dan Oscarsson.
+       Fix core dump if getgrgid returns a null group list (as opposed
+               to an empty group list, that is, a pointer to a list
+               with no members).  Fix from Andrew Chang of Sun Microsystems.
+       Fix possible core dump if malloc fails -- if the malloc in xalloc
+               failed, it called syserr which called newstr which called
+               xalloc....  The newstr is now avoided for "panic" messages.
+               Reported by Stuart Kemp of James Cook University.
+       Improve connection cache timeouts; previously, they were not even
+               checked if you were delivering to anything other than an
+               IPC-connected host, so a series of (say) local mail
+               deliveries could cause cached connections to be open
+               much longer than the specified timeout.
+       If an incoming message exceeds the maximum message size, stop
+               writing the incoming bytes to the queue data file, since
+               this can fill your mqueue partition -- this is a possible
+               denial-of-service attack.
+       Don't reject all numeric local user names unless HESIOD is
+               defined.  It turns out that Posix allows all-numeric
+               user names.  Fix from Tony Sanders of BSDI.
+       Add service switch support.  If the local OS has a service
+               switch (e.g., /etc/nsswitch.conf on Solaris or /etc/svc.conf
+               on DEC systems) that will be used; otherwise, it falls back
+               to using a local mechanism based on the ServiceSwitchFile
+               option (default: /etc/service.switch).  For example, if the
+               service switch lists "files" and "nis" for the aliases
+               service, that will be the default lookup order.  the "files"
+               ("local" on DEC) service type expands to any alias files
+               you listed in the configuration file, even if they aren't
+               actually file lookups.
+       Option I (NameServerOptions) no longer sets the "UseNameServer"
+               variable which tells whether or not DNS should be considered
+               canonical.  This is now determined based on whether or not
+               "dns" is in the service list for "hosts".
+       Add preliminary support for the ESMTP "DSN" extension (Delivery
+               Status Notifications).  DSN notifications override
+               Return-Receipt-To: headers, which are bogus anyhow.
+       Add T=mts-name-type/address-type/diagnostic-type keyletter to mailer
+               definitions to define the types used in DSN returns for
+               MTA names, addresses, and diagnostics respectively.
+       Extend heuristic to force running in ESMTP mode to look for the
+               five-character string "ESMTP" anywhere in the 220 greeting
+               message (not just the second line).  This is to provide
+               better compatibility with other ESMTP servers.
+       Print sequence number of job when running the queue so you can
+               easily see how much progress you have made.  Suggested
+               by Peter Wemm of DIALix.
+       Map newlines to spaces in logged message-ids; some versions of
+               syslog truncate the rest of the line after newlines.
+               Suggested by Fletcher Mattox of U. Texas.
+       Move up forking for job runs so that if a message is split into
+               multiple envelopes you don't get "fork storms" -- this
+               also improves the connection cache utilization.
+       Accept "<<>>", "<<<>>>", and so forth as equivalent to "<>" for
+               the purposes of refusing to send error returns.  Suggested
+               by Motonori Nakamura of Ritsumeikan University.
+       Relax rules on when a file can be written when referenced from
+               the aliases file: use the default uid/gid instead of the
+               real uid/gid.  This allows you to create a file owned by
+               and writable only by the default uid/gid that will work
+               all the time (without having the setuid bit set).  Change
+               suggested by Shau-Ping Lo and Andrew Cheng of Sun
+               Microsystems.
+       Add "DialDelay" option (no short name) to provide an "extra"
+               delay for dial on demand systems.  If this is non-zero
+               and a connect fails, sendmail will wait this long and
+               then try again.  If it takes longer than the kernel
+               timeout interval to establish the connection, this
+               option can give the network software time to establish
+               the link.  The default units are seconds.
+       Move logging of sender information to be as early as possible;
+               previously, it could be delayed a while for SMTP mail
+               sent to aliases.  Suggested by Brad Knowles of the
+               Defense Information Systems Agency.
+       Call res_init() before setting RES_DEBUG; this is required by
+               BIND 4.9.3, or so I'm told.  From Douglas Anderson of
+               the National Computer Security Center.
+       Add xdelay= field in logs -- this is a transaction delay, telling
+               you how long it took to deliver to this address on the
+               last try.  It is intended to be used for sorting mailing
+               lists to favor "quick" addresses.  Provided for use by
+               the mailprio scripts (see below).
+       If a map cannot be opened, and that map is non-optional, and
+               an address requires that map for resolution, queue the
+               map instead of bouncing it.  This involves creating a
+               pseudo-class of maps called "bogus-map" -- if a required
+               map cannot be opened, the class is changed to bogus-map;
+               all queries against bogus-map return "tempfail".  The
+               bogus-map class is not directly accessible.  A sample
+               implementation was donated by Jem Taylor of Glasgow
+               University Computing Service.
+       Fix a possible core dump when mailing to a program that talks
+               SMTP on its standard input.  Fix from Keith Moore of
+               the University of Kentucky.
+       Make it possible to resolve filenames to $#local $: @ /filename;
+               previously, the "@" would cause it to not be recognized
+               as a file.  Problem noted by Brian Hill of U.C. Davis.
+       Accept a -1 signal to re-exec the daemon.  This only works if
+               argv[0] is a full path to sendmail.
+       Fix bug in "addr=..." field in O option on little-endian machines
+               -- the network number wasn't being converted to network
+               byte order.  Patch from Kurt Lidl of Pix Technologies
+               Corporation.
+       Pre-initialize the resolver early on; this is to avoid a bug with
+               BIND 4.9.3 that can cause the _res.retry field to get
+               reset to zero, causing all name server lookups to time
+               out.  Fix from Matt Day of Artisoft.
+       Restore T line (trusted users) in config file -- but instead of
+               locking out the -f flag, they just tell whether or not
+               an X-Authentication-Warning: will be added.  This really
+               just creates new entries in class 't', so "Ft/file/name"
+               can be used to read trusted user names from a file.
+               Trusted users are also allowed to execute programs even
+               if they have a shell that isn't in /etc/shells.
+       Improve NEWDB alias file rebuilding so it will create them
+               properly if they do not already exist.  This had been
+               a MAYBENEXTRELEASE feature in 8.6.9.
+       Check for @:@ entry in NIS maps before starting up to avoid
+               (but not prevent, sigh) race conditions.  This ought to
+               be handled properly in ypserv, but isn't.  Suggested by
+               Michael Beirne of Motorola.
+       Refuse connections if there isn't enough space on the filesystem
+               holding the queue.  Contributed by Robert Dana of Wolf
+               Communications.
+       Skip checking for directory permissions in the path to a file
+               when checking for file permissions iff setreuid()
+               succeeded -- it is unnecessary in that case.  This avoids
+               significant performance problems when looking for .forward
+               files.  Based on a suggestion by Win Bent of USC.
+       Allow symbolic ruleset names.  Syntax can be "Sname" to get an
+               arbitrary ruleset number assigned or "Sname = integer"
+               to assign a specific ruleset number.  Reference is
+               $>name_or_number.  Names can be composed of alphas, digits,
+               underscore, or hyphen (first character must be non-numeric).
+       Allow -o flag on AliasFile lines to make the alias file optional.
+               From Bryan Costales of ICSI.
+       Add NoRecipientAction option to handle the case where there is
+               no legal recipient header in the message.  It can take
+               on values:
+                 None                  Leave the message as is.  The
+                                       message will be passed on even
+                                       though it is in technically
+                                       illegal syntax.
+                 Add-To                Add a To: header with any
+                                       recipients that it can find from
+                                       the envelope.  This risks exposing
+                                       Bcc: recipients.
+                 Add-Apparently-To     Add an Apparently-To: header.  This
+                                       has almost no redeeming social value,
+                                       and is provided only for back
+                                       compatibility.
+                 Add-To-Undisclosed    Add a header reading
+                                       To: undisclosed-recipients:;
+                                       which will have the effect of
+                                       making the message legal without
+                                       exposing Bcc: recipients.
+                 Add-Bcc               To add an empty Bcc: header.
+                                       There is a chance that mailers down
+                                       the line will delete this header,
+                                       which could cause exposure of Bcc:
+                                       recipients.
+               The default is NoRecipientAction=None.
+       Truncate (rather than delete) Bcc: lines in the header.  This
+               should prevent later sendmails (at least, those that don't
+               themselves delete Bcc:) from considering this message to
+               be non-conforming -- although it does imply that non-blind
+               recipients can see that a Bcc: was sent, albeit not to whom.
+       Add SafeFileEnvironment option.  If declared, files named as delivery
+               targets must be regular files in addition to the regular
+               checks.  Also, if the option is non-null then it is used as
+               the name of a directory that is used as a chroot(2)
+               environment for the delivery; the file names listed in an
+               alias or forward should include the name of this root.
+               For example, if you run with
+                       O SafeFileEnvironment=/arch
+               then aliases should reference "/arch/rest/of/path".  If a
+               value is given, sendmail also won't try to save to
+               /usr/tmp/dead.letter (instead it just leaves the job in the
+               queue as Qfxxxxxx).  Inspired by *Hobbit*'s sendmail patch kit.
+       Support -A flag for alias files; this will comma concatenate like
+               entries.  For example, given the aliases:
+                       list: member1
+                       list: member2
+               and an alias file declared as:
+                       OAhash:-A /etc/aliases
+               the final alias inserted will be "list: member1,member2";
+               without -A you will get an error on the second and subsequent
+               alias for "list".  Contributed by Bryan Costales of ICSI.
+       Line-buffer transcript file.  Suggested by Liudvikas Bukys.
+       Fix a problem that could cause very long addresses to core dump in
+               some special circumstances.  Problem pointed out by Allan
+               Johannesen.
+       (Internal change.)  Change interface to expand() (macro expansion)
+               to be simpler and more consistent.
+       Delete check for funny qf file names.  This didn't really give
+               any extra security and caused some people some problems.
+               (If you -really- want this, define PICKY_QF_NAME_CHECK
+               at compile time.)  Suggested by Kyle Jones of UUNET.
+       (Internal change.)  Change EF_NORETURN to EF_NO_BODY_RETN and
+               merge with DSN code; this is simpler and more consistent.
+               This may affect some people who have written their own
+               checkcompat() routine.
+       (Internal change.)  Eliminate `D' line in qf file.  The df file
+               is now assumed to be the same name as the qf file (with
+               the `q' changed to a `d', of course).
+       Avoid forking for delivery if all recipient mailers are marked as
+               "expensive" -- this can be a major cost on some systems.
+               Essentially, this forces sendmail into "queue only" mode
+               if all it is going to do is queue anyway.
+       Avoid sending a null message in some rather unusual circumstances
+               (specifically, the RCPT command returns a temporary
+               failure but the connection is lost before the DATA
+               command).  Fix from Scott Hammond of Secure Computing
+               Corporation.
+       Change makesendmail to use a somewhat more rational naming scheme:
+               Makefiles and obj directories are named $os.$rel.$arch,
+               where $os is the operating system (e.g., SunOS), $rel is
+               the release number (e.g., 5.3), and $arch is the machine
+               architecture (e.g., sun4).  Any of these can be omitted,
+               and anything after the first dot in a release number can
+               be replaced with "x" (e.g., SunOS.4.x.sun4).  The previous
+               version used $os.$arch.$rel and was rather less general.
+       Change makesendmail to do a "make depend" in the target directory
+               when it is being created.  This involves adding an empty
+               "depend:" entry in most Makefiles.
+       Ignore IDENT return value if the OSTYPE field returns "OTHER",
+               as indicated by RFC 1413.  Pointed out by Kari Hurtta
+               of the Finnish Meteorological Institute.
+       Fix problem that could cause multiple responses to DATA command
+               on header syntax errors (e.g., lines beginning with colons).
+               Problem noted by Jens Thomassen of the University of Oslo.
+       Don't let null bytes in headers cause truncation of the rest of
+               the header.
+       Log Authentication-Warning:s.  Suggested by Motonori Nakamura.
+       Increase timeouts on message data puts to allow time for receivers
+               to canonify addresses in headers on the fly.  This is still
+               a rather ugly heuristic.  From Motonori Nakamura.
+       Add "HasWildcardMX" suboption to ResolverOptions; if set, MX
+               records are not used when canonifying names, and when MX
+               lookups are done for addressing they must be fully
+               qualified.  This is useful if you have a wildcard MX record,
+               although it may cause other problems.  In general, don't use
+               wildcard MX records.  Patch from Motonori Nakamura.
+       Eliminate default two-line SMTP greeting message.  Instead of
+               adding an extra "ESMTP spoken here" line, the word "ESMTP"
+               is added between the first and second word of the first
+               line of the greeting message (i.e., immediately after the
+               host name).  This eliminates the need for the BROKEN_SMTP_PEERS
+               compile flag.  Old sendmails won't see the ESMTP, but that's
+               acceptable because SIZE was the only useful extension that
+               old sendmails understand.
+       Avoid gethostbyname calls on UNIX domain sockets during SIGUSR1
+               invoked state dumps.  From Masaharu Onishi.
+       Allow on-line comments in .forward and :include: files; they are
+               introduced by the string "<LWSP>#@#<LWSP>", where <LWSP>
+               is a space or a tab.  This is intended for native
+               representation of non-ASCII sets such as Japanese, where
+               existing encodings would be unreadable or would lose
+               data -- for example,
+                <motonori@cs.ritsumei.ac.jp> NAKAMURA Motonori
+                                       (romanized/less information)
+                <motonori@cs.ritsumei.ac.jp> =?ISO-2022-JP?B?GyRCQ2ZCPBsoQg==?=
+                                             =?ISO-2022-JP?B?GyRCQUdFNRsoQg==?=
+                                       (with MIME encoding, not human readable)
+                <motonori@cs.ritsumei.ac.jp> #@# ^[$BCfB<^[(B ^[$BAGE5^[(B
+                                       (native encoding with ISO-2022-JP)
+               The last form is human readable in the Japanese environment.
+               Based on a fix from (surprise!) Motonori Nakamura.
+       Don't make SMTP error returns on MAIL FROM: line be "sticky" for all
+               messages to that host; these are most frequently associated
+               with addresses rather than the host, with the exception of
+               421 (service shutting down).  The effect was to cause queues
+               to sometimes take an excessive time to flush.  Reported by
+               Robert Sargent of Southern Geographics Technologies and
+               Eric Prestemon of American University.
+       Add Nice=N mailer option to set the niceness at which a mailer will
+               run.  This is actually a relative niceness (that is, an
+               increment on the background value).
+       Log queue runs that are skipped due to high loads.  They are logged
+               at LOG_INFO priority iff the log level is > 8.  Contributed
+               by Bruce Nagel of Data General.
+       Allow the error mailer to accept a DSN-style error status code
+               instead of an sysexits status code in the host part.
+               Anything with a dot will be interpreted as a DSN-style code.
+       Add new mailer flag: F=3 will tell translations to Quoted-Printable
+               to encode characters that might be munged by an EBCDIC system
+               in addition to the set required by RFC 1521.  The additional
+               characters are !, ", #, $, @, [, \, ], ^, `, {, |, }, and ~.
+               (Think of "IBM 360" as the mnemonic for this flag.)
+       Change check for mailing to files to look for a pathname of [FILE]
+               rather than looking for the mailer named *file*.  The mapping
+               of leading slashes still goes to the *file* mailer.  This
+               allows you to implement the *file* mailer as a separate
+               program, for example, to insert a Content-Length: header
+               or do special security policy.  However, note that the usual
+               initial checking for the file permissions is still done, and
+               the program in question needs to be very careful about how
+               it does the file write to avoid security problems.
+       Be able to read ~root/.forward even if the path isn't accessible to
+               regular users.  This is disrecommended because sendmail
+               sometimes does not run as root (e.g., when an unsafe option
+               is specified on the command line), but should otherwise be
+               safe because .forward files must be owned by the user for
+               whom mail is being forwarded, and cannot be a symbolic link.
+               Suggested by Forrest Aldrich of Wang Laboratories.
+       Add new "HostsFile" option that is the pathname to the /etc/hosts
+               file.  This is used for canonifying hostnames when the
+               service type is "files".
+       Implement programs on F (read class from file) line.  The syntax is
+               Fc|/path/to/program to read the output from the program
+               into class "c".
+       Probe the network interfaces to find alternate names for this
+               host.  Requires the SIOCGIFCONF ioctl call.  Code
+               contributed by SunSoft.
+       Add "E" configuration line to set or propogate environment
+               variables into children.  "E<envar>" will propogate
+               the named variable from the environment when sendmail
+               was invoked into any children it calls; "E<envar>=<value>"
+               sets the named variable to the indicated value.  Any
+               variables not explicitly named will not be in the child
+               environment.  However, sendmail still forces an
+               "AGENT=sendmail" environment variable, in part to enforce
+               at least one environment variable, since many programs and
+               libraries die horribly if this is not guaranteed.
+       Change heuristic for rebuilding both NEWDB and NDBM versions of
+               alias databases -- new algorithm looks for the substring
+               "/yp/" in the file name.  This is more portable and involves
+               less overhead.  Suggested by Motonori Nakamura.
+       Dynamically allocate the queue work list so that you don't lose
+               jobs in large queue runs.  The old QUEUESIZE compile parameter
+               is replaced by QUEUESEGSIZE (the unit of allocation, which
+               should not need to be changed) and the MaxQueueRunSize option,
+               which is the absolute maximum number of jobs that will ever
+               be handled in a single queue run.  Based on code contributed
+               by Brian Coan of the Institute for Global Communications.
+       Log message when a message is dropped because it exceeds the maximum
+               message size.  Suggested by Leo Bicknell of Virginia Tech.
+       Allow trusted users (those on a T line or in $=t) to use -bs without
+               an X-Authentication-Warning: added.  Suggested by Mark Thomas
+               of Mark G. Thomas Consulting.
+       Announce state of compile flags on -d0.1 (-d0.10 throws in the
+               OS-dependent defines).  The old semantic of -d0.1 to not
+               run the daemon in background has been moved to -d99.100,
+               and the old 52.5 flag (to avoid disconnect() from closing
+               all output files) has been moved to 52.100.  This makes
+               things more consistent (flags below .100 don't change
+               semantics) and separates out the backgrounding so that
+               it doesn't happen automatically on other unrelated debugging
+               flags.
+       If -t is used but no addresses are found in the header, give an
+               error message rather than just doing nothing.  Fix from
+               Motonori Nakamura.
+       On systems (like SunOS) where the effective gid is not necessarily
+               included in the group list returned by getgroups(), the
+               `restrictmailq' option could sometimes cause an authorized
+               user to not be able to use `mailq'.  Fix from Charles Hannum
+               of MIT.
+       Allow symbolic service names for [IPC] mailers.  Suggested by
+               Gerry Magennis of Logica International.
+       Add DontExpandCnames option to prevent $[ ... $] from expanding CNAMEs
+               when running DNS.  For example, if the name FTP.Foo.ORG is
+               a CNAME for Cruft.Foo.ORG, then when sitting on a machine in
+               the Foo.ORG domain a lookup of "FTP" returns "Cruft.Foo.ORG"
+               if this option is not set, or "FTP.Foo.ORG" if it is set.
+               This is technically illegal under RFC 822 and 1123, but the
+               IETF is moving toward legalizing it.  Note that turning on
+               this option is not sufficient to guarantee that a downstream
+               neighbor won't rewrite the address for you.
+       Add "-m" flag to makesendmail script -- this tells you what object
+               directory and Makefile it will use, but doesn't actually do
+               the make.
+       Do some additional checking on the contents of the qf file to try
+               to detect attacks against the qf file.  In particular,
+               abort on any line beginning "From ", and add an "end of
+               file" line -- any data after that line is prohibited.
+       Always use /etc/sendmail.cf, regardless of the arbitrary vendor
+               choices.  This can be overridden in the Makefile by using
+               either -DUSE_VENDOR_CF_PATH to get the vendor location
+               (to the extent that we know it) or by defining
+               _PATH_SENDMAILCF (which is a "hard override").  This allows
+               sendmail 8 to have more consistent installation instructions.
+       Allow macros on `K' line in config file.  Suggested by Andrew Chang
+               of Sun Microsystems.
+       Improved symbol table hash function from Eric Wassenaar.  This one
+               is at least 50% faster.
+       Fix problem that didn't notice that timeout on file open was a
+               transient error.  Fix from Larry Parmelee of Cornell
+               University.
+       Allow comments (lines beginning with a `#') in files read for
+               classes.  Suggested by Motonori Nakamura.
+       Make SIGINT (usually ^C) in test mode return to the prompt instead
+               of dropping out entirely.  This makes testing some of the
+               name server lookups easier to deal with when there are
+               hung servers.  From Motonori Nakamura.
+       Add new ${opMode} macro that is set to the current operation mode
+               (e.g., `s' for -bs, `t' for -bt, etc.).  Suggested by
+               Claude Marinier <MARINIER@emp.ewd.dreo.dnd.ca>.
+       Add new delivery mode (Odd) that defers all map lookups to queue runs.
+               Kind of like queue-only mode (Odq) except it tries to avoid
+               any external service requests; for dial-on-demand hosts that
+               want to minimize DNS lookups when mail is being queued.  For
+               this to work you will also have to make sure that gethostbyname
+               of your local host name does not do a DNS lookup.
+       Improved handling of "out of space" conditions from John Myers of
+               Carnegie Mellon.
+       Improved security for mailing to files on systems that have fchmod(2)
+               support.
+       Improve "cannot send message for N days" message -- now says "could
+               not send for past N days".  Suggested by Tom Moore of AT&T
+               Global Information Solutions.
+       Less misleading Subject: line on messages sent to postmaster only.
+               From Motonori Nakamura.
+       Avoid duplicate error messages on bad command line flags.  From
+               Motonori Nakamura.
+       Better error message for case where ruleset 0 falls off the end
+               or otherwise does not resolve to a canonical triple.
+       Fix a problem that could cause multiple bounce messages if a bad
+               address was sent along with a good address to an SMTP
+               site where that SMTP site returned a 4yz code in response
+               to the final dot of the data.  Problem reported by David
+               James of British Telecom.
+       Add "volatile" declarations so that gcc -O2 will work.  Patches
+               from Alexander Dupuy of System Management ARTS.
+       Delete duplicates in MX lists -- believe it or not, there are sites
+               that list the same host twice in an MX list.  This deletion
+               only works on adjacent preferences, so an MX list that
+               had A=5, B=10, A=15 would leave both As, but one that had
+               A=5, A=10, B=15 would reduce to A, B.  This is intentional,
+               just in case there is something wierd I haven't thought of.
+               Suggested by Barry Shein of Software Tool & Die.
+       SECURITY: .forward files cannot be symbolic links.  If they are,
+               a bad guy can read your private files.
+       PORTABILITY FIXES:
+               Solaris 2 from Rob McMahon <cudcv@csv.warwick.ac.uk>.
+               System V Release 4 from Motonori Nakamura of Ritsumeikan
+                       University.  This expands the disk size
+                       checking to include all (?) SVR4 configurations.
+               System V Release 4 from Kimmo Suominen -- initgroups(3)
+                       and setrlimit(2) are both available.
+               System V Release 4 from sob@sculley.ffg.com -- some versions
+                       apparently "have EX_OK defined in other headerfiles."
+               Linux Makefile typo.
+               Linux getusershell(3) is broken in Slackware 2.0 --
+                       from Andrew Pam of Xanadu Australia.
+               More Linux tweaking from John Kennedy of California State
+                       University, Chico.
+               Cray changes from Eric Wassenaar:  ``On Cray, shorts,
+                       ints, and longs are all 64 bits, and all structs
+                       are multiples of 64 bits.  This means that the
+                       sizeof operator returns only multiples of 8.
+                       This requires adaptation of code that really
+                       deals with 32 bit or 16 bit fields, such as IP
+                       addresses or nameserver fields.''
+               DG/UX 5.4.3 from Mark T. Robinson <mtr@ornl.gov>.  To
+                       get the old behaviour, use -DDGUX_5_4_2.
+               DG/UX hack: add _FORCE_MAIL_LOCAL_=yes environment
+                       variable to fix bogus /bin/mail behaviour.
+               Tandem NonStop-UX from Rick McCarty <mccarty@mpd.tandem.com>.
+                       This also cleans up some System V Release 4 compile
+                       problems.
+               Solaris 2: sendmail.cw file should be in /etc/mail to
+                       match all the other configuration files.  Fix
+                       from Glenn Barry of Emory University.
+               Solaris 2.3: compile problem in conf.c.  Fix from Alain
+                       Nissen of the University of Liege, Belgium.
+               Ultrix: freespace calculation was incorrect.  Fix from
+                       Takashi Kizu of Osaka University.
+               SVR4: running in background gets a SIGTTOU because the
+                       emulation code doesn't realize that "getpeername"
+                       doesn't require reading the file.  Fix from Peter
+                       Wemm of DIALix.
+               Solaris 2.3: due to an apparent bug in the socket emulation
+                       library, sockets can get into a "wedged" state where
+                       they just return EPROTO; closing and re-opening the
+                       socket clears the problem.  Fix from Bob Manson
+                       of Ohio State University.
+               Hitachi 3050R & 3050RX running HI-UX/WE2: portability
+                       fixes from Akihiro Hashimoto ("Hash") of Chiba
+                       University.
+               AIX changes to allow setproctitle to work from Rainer Schöpf
+                       of Zentrum für Datenverarbeitung der Universität
+                       Mainz.
+               AIX changes for load average from Ed Ravin of NASA/Goddard.
+               SCO Unix from Chip Rosenthal of Unicom (code was using the
+                       wrong statfs call).
+               ANSI C fixes from Adam Glass (NetBSD project).
+               Stardent Titan/ANSI C fixes from Kate Hedstrom of Rutgers
+                       University.
+               DG-UX fixes from Bruce Nagel of Data General.
+               IRIX64 updates from Mark Levinson of the University of
+                       Rochester Medical Center.
+               Altos System V (``the first UNIX/XENIX merge the Altos
+                       did for their Series 1000 & Series 2000 line;
+                       their merged code was licenced back to AT&T and
+                       Microsoft and became System V release 3.2'') from
+                       Tim Rice <timr@crl.com>.
+               OSF/1 running on Intel Paragon from Jeff A. Earickson
+                       <jeff@ssd.intel.com> of Intel Scalable Systems
+                       Divison.
+               Amdahl UTS System V 2.1.5 (SVr3-based) from Janet Jackson
+                       <janet@dialix.oz.au>.
+               System V Release 4 (statvfs semantic fix) from Alain
+                       Durand of I.M.A.G.
+               HP-UX 10.x multiprocessor load average changes from
+                       Scott Hutton and Jeff Sumler of Indiana University.
+               Cray CSOS from Scott Bolte of Cray Computer Corporation.
+               Unicos 8.0 from Douglas K. Rand of the University of North
+                       Dakota, Scientific Computing Center.
+               Solaris 2.4 fixes from Sanjay Dani of Dani Communications.
+               ConvexOS 11.0 from Christophe Wolfhugel.
+               IRIX 4.0.5 from David Ashton-Reader of CADcentre.
+               ISC UNIX from J. J. Bailey.
+               HP-UX 9.xx on the 8xx series machines from Remy Giraud
+                       of Meteo France.
+               HP-UX configuration from Tom Lane <tgl@sss.pgh.pa.us>.
+               IRIX 5.2 and 5.3 from Kari E. Hurtta.
+               FreeBSD 2.0 from Mike Hickey of Federal Data Corporation.
+               Sony NEWS-OS 4.2.1R and 6.0.3 from Motonori Nakamura.
+               Omron LUNA unios-b, mach from Motonori Nakamura.
+               NEC EWS-UX/V 4.2 from Motonori Nakamura.
+               NeXT 2.1 from Bryan Costales.
+               AUX patch thanks to Mike Erwin of Apple Computer.
+               HP-UX 10.0 from John Beck of Hewlett-Packard.
+               Ultrix: allow -DBROKEN_RES_SEARCH=0 if you are using a
+                       non-DEC resolver.  Suggested by Allan Johannesen.
+               UnixWare 2.0 fixes from Petr Lampa of the Technical
+                       University of Brno (Czech Republic).
+               KSR OS 1.2.2 support from Todd Miller of the University
+                       of Colorado.
+               UX4800 support from Kazuhisa Shimizu of NEC.
+       MAKEMAP: allow -d flag to allow insertion of duplicate aliases
+               in type ``btree'' maps.  The semantics of this are undefined
+               for regular maps, but it can be useful for the user database.
+       MAKEMAP: lock database file while rebuilding to avoid sendmail
+               lookups while the rebuild is going on.  There is a race
+               condition between the open(... O_TRUNC ...) and the lock
+               on the file, but it should be quite small.
+       SMRSH: sendmail restricted shell added to the release.  This can
+               be used as an alternative to /bin/sh for the "prog" mailer,
+               giving the local administrator more control over what
+               programs can be run from sendmail.
+       MAIL.LOCAL: add this local mailer to the tape.  It is not really
+               part of the release proper, and isn't fully supported; in
+               particular, it does not run on System V based systems and
+               never will.
+       CONTRIB: a patch to rmail.c from Bill Gianopoulos of Raytheon
+               to allow rmail to compile on systems that don't have
+               function prototypes and systems that don't have snprintf.
+       CONTRIB: add the "mailprio" scripts that will help you sort mailing
+               lists by transaction delay times so that addresses that
+               respond quickly get sent first.  This is to prevent very
+               sluggish servers from delaying other peoples' mail.
+               Contributed by Tony Sanders of BSDI.
+       CONTRIB: add the "bsdi.mc" file as contributed by Tony Sanders
+               of BSDI.  This has a lot of comments to help people out.
+       CONFIG: Don't have .mc files include(../m4/cf.m4) -- instead,
+               put this on the m4 command line.  On GNU m4 (which
+               supports the __file__ primitive) you can run m4 in an
+               arbitrary directory -- use either:
+                       m4 ${CFDIR}/m4/cf.m4 config.mc > config.cf
+               or
+                       m4 -I${CFDIR} m4/cf.m4 config.mc > config.cf
+               On other versions of m4 that don't support __file__, you
+               can use:
+                       m4 -D_CF_DIR_=${CFDIR}/ ${CFDIR}/m4/cf.m4 ...
+               (Note the trailing slash on the _CF_DIR_ definition.)
+               Old versions of m4 will default to _CF_DIR_=.. for back
+               compatibility.
+       CONFIG: fix mail from <> so it will properly convert to
+               MAILER-DAEMON on local addresses.
+       CONFIG: fix code that was supposed to catch colons in host
+               names.  Problem noted by John Gardiner Myers of CMU.
+       CONFIG: allow use of SMTP_MAILER_MAX in nullclient configuration.
+               From Paul Riddle of the University of Maryland, Baltimore
+               County.
+       CONFIG: Catch and reject "." as a host address.
+       CONFIG: Generalize domaintable to look up all domains, not
+               just unqualified ones.
+       CONFIG: Delete OLD_SENDMAIL support -- as near as I can tell, it
+               was never used and didn't work anyway.
+       CONFIG: Set flags A, w, 5, :, /, |, and @ on the "local" mailer
+               and d on all mailers in the UUCP class.
+       CONFIG: Allow "user+detail" to be aliased specially: it will first
+               look for an alias for "user+detail", then for "user+*", and
+               finally for "user".  This is intended for forwarding mail
+               for system aliases such as root and postmaster to a
+               centralized hub.
+       CONFIG: add confEIGHT_BIT_HANDLING to set option 8 (see above).
+       CONFIG: add smtp8 mailer; this has the F=8 (just-send-8) flag set.
+               The F=8 flag is also set on the "relay" mailer, since
+               this is expected to be another sendmail.
+       CONFIG: avoid qualifying all UUCP addresses sent via SMTP with
+               the name of the UUCP_RELAY -- in some cases, this is the
+               wrong value (e.g., when we have local UUCP connections),
+               and this can create unreplyable addresses.  From Chip
+               Rosenthal of Unicom.
+       CONFIG: add confRECEIVED_HEADER to change the format of the
+               Received: header inserted into all messages. Suggested by
+               Gary Mills of the University of Manitoba.
+       CONFIG: Make "notsticky" the default; use FEATURE(stickyhost)
+               to get the old behaviour.  I did this upon observing
+               that almost everyone needed this feature, and that the
+               concept I was trying to make happen didn't work with
+               some user agents anyway.  FEATURE(notsticky) still works,
+               but it is a no-op.
+       CONFIG: Add LUSER_RELAY -- the host to which unrecognized user
+               names are sent, rather than immediately diagnosing them
+               as User Unknown.
+       CONFIG: Add SMTP_MAILER_ARGS, ESMTP_MAILER_ARGS, SMTP8_MAILER_ARGS,
+               and RELAY_MAILER_ARGS to set the arguments for the
+               indicated mailers.  All default to "IPC $h".  Patch from
+               Larry Parmelee of Cornell University.
+       CONFIG: pop mailer needs F=n flag to avoid "annoying side effects
+               on the client side" and F=P to get an appropriate
+               return-path.  From Kimmo Suominen.
+       CONFIG: add FEATURE(local_procmail) to use the procmail program
+               as the local mailer.  For addresses of the form "user+detail"
+               the "detail" part is passed to procmail via the -a flag.
+               Contributed by Kimmo Suominen.
+       CONFIG: add MAILER(procmail) to add an interface to procmail for
+               use from mailertables.  This lets you execute arbitrary
+               procmail scripts.  Contributed by Kimmo Suominen.
+       CONFIG: add T= fields (MTS type) to local, smtp, and uucp mailers.
+       CONFIG: add OSTYPE(ptx2) for DYNIX/ptx 2.x from Sequent.  From
+               Paul Southworth of CICNet Systems Support.
+       CONFIG: use -a$g as default to UUCP mailers, instead of -a$f.
+               This causes the null return path to be rewritten as
+               MAILER-DAEMON; otherwise UUCP gets horribly confused.
+               From Michael Hohmuth of Technische Universitat Dresden.
+       CONFIG: Add FEATURE(bestmx_is_local) to cause any hosts that
+               list us as the best possible MX record to be treated as
+               though they were local (essentially, assume that they
+               are included in $=w).  This can cause additional DNS
+               traffic, but is easier to administer if this fits your
+               local model.  It does not work reliably if there are
+               multiple hosts that share the best MX preference.
+               Code contributed by John Oleynick of Rutgers.
+       CONFIG: Add FEATURE(smrsh) to use smrsh (the SendMail Restricted
+               SHell) instead of /bin/sh as the program used for delivery
+               to programs.  If an argument is included, it is used as
+               the path to smrsh; otherwise, /usr/local/etc/smrsh is
+               assumed.
+       CONFIG: Add LOCAL_MAILER_MAX and PROCMAILER_MAILER_MAX to limit the
+               size of messages to the local and procmail mailers
+               respectively.  Contributed by Brad Knowles of the Defense
+               Information Systems Agency.
+       CONFIG: Handle leading ``phrase:'' and trailing ``;'' as comments
+               (just like text outside of angle brackets) in order to
+               properly deal with ``group: addr1, ... addrN;'' syntax.
+       CONFIG: Require OSTYPE macro (the defaults really don't apply to
+               any real systems any more) and tweak the DOMAIN macro
+               so that it is less likely that users will accidently use
+               the Berkeley defaults.  Also, create some generic files
+               that really can be used in the real world.
+       CONFIG: Add new configuration macros to set character sets for
+               messages _arriving from_ various mailers: LOCAL_MAILER_CHARSET,
+               SMTP_MAILER_CHARSET, and UUCP_MAILER_CHARSET.
+       CONFIG: Change UUCP_MAX_SIZE to UUCP_MAILER_MAX for consistency.
+               The old name will still be accepted for a while at least.
+       CONFIG: Implement DECNET_RELAY as spec for host to which DECNET
+               mail (.DECNET pseudo-domain or node::user) will be sent.
+               As with all relays, it can be ``mailer:hostname''.  Suggested
+               by Scott Hutton.
+       CONFIG: Add MAILER(mail11) to get DECnet support.  Code contributed
+               by Barb Dijker of Labyrinth Computer Services.
+       CONFIG: change confCHECK_ALIASES to default to False -- it has poor
+               performance for large alias files, and this confused many
+               people.
+       CONFIG: Add confCF_VERSION to append local information to the
+               configuration version number displayed during SMTP startup.
+       CONFIG: fix some.newsgroup.usenet@local.host syntax (previously it
+               would only work when locally addressed.  Fix from
+               Edvard Tuinder of Cistron Internet Services.
+       CONFIG: use ${opMode} to avoid error on .REDIRECT addresses if option
+               "n" (CheckAlaises) is set when rebuilding alias database.
+               Based on code contributed by Claude Marinier.
+       CONFIG: Allow mailertable to have values of the form
+               ``error:code message''.  The ``code'' is a status code
+               derived from the sysexits codes -- e.g., NOHOST or UNAVAILABLE.
+               Contributed by David James <dwj@agw.bt.co.uk>.
+       CONFIG: add MASQUERADE_DOMAIN(domain list) to extend the list of
+               sender domains that will be replaced with the masquerade name.
+               These domains will not be treated as local, but if mail passes
+               through with sender addresses in those domains they will be
+               replaced by the masquerade name.  These can also be specified
+               in a file using MASQUERADE_DOMAIN_FILE(filename).
+       CONFIG: add FEATURE(masquerade_envelope) to masquerade the envelope
+               as well as the header.  Substantial improvements to this
+               code were contributed by Per Hedeland.
+       CONFIG: add MAILER(phquery) to define a new "ph" mailer; this can be
+               accessed from a mailertable to do CCSO ph lookups.  Contributed
+               by Kimmo Suominen.
+       CONFIG: add MAILER(cyrus) to define a new Cyrus mailer; this can be
+               used to define cyrus and cyrusbb mailers (for IMAP support).
+               Contributed by John Gardiner Myers of Carnegie Mellon.
+       CONFIG: add confUUCP_MAILER to select default mailer to use for
+               UUCP addressing.  Suggested by Tom Moore of AT&T GIS.
+       NEW FILES:
+               cf/cf/cs-hpux10.mc
+               cf/cf/cs-solaris2.mc
+               cf/cf/cyrusproto.mc
+               cf/cf/generic-bsd4.4.mc
+               cf/cf/generic-hpux10.mc
+               cf/cf/generic-hpux9.mc
+               cf/cf/generic-osf1.mc
+               cf/cf/generic-solaris2.mc
+               cf/cf/generic-sunos4.1.mc
+               cf/cf/generic-ultrix4.mc
+               cf/cf/huginn.cs.mc
+               cf/domain/berkeley-only.m4
+               cf/domain/generic.m4
+               cf/feature/bestmx_is_local.m4
+               cf/feature/local_procmail.m4
+               cf/feature/masquerade_envelope.m4
+               cf/feature/smrsh.m4
+               cf/feature/stickyhost.m4
+               cf/feature/use_ct_file.m4
+               cf/m4/cfhead.m4
+               cf/mailer/cyrus.m4
+               cf/mailer/mail11.m4
+               cf/mailer/phquery.m4
+               cf/mailer/procmail.m4
+               cf/ostype/amdahl-uts.m4
+               cf/ostype/bsdi2.0.m4
+               cf/ostype/hpux10.m4
+               cf/ostype/irix5.m4
+               cf/ostype/isc4.1.m4
+               cf/ostype/ptx2.m4
+               cf/ostype/unknown.m4
+               contrib/bsdi.mc
+               contrib/mailprio
+               contrib/rmail.oldsys.patch
+               mail.local/mail.local.0
+               makemap/makemap.0
+               smrsh/README
+               smrsh/smrsh.0
+               smrsh/smrsh.8
+               smrsh/smrsh.c
+               src/Makefiles/Makefile.CSOS
+               src/Makefiles/Makefile.EWS-UX_V
+               src/Makefiles/Makefile.HP-UX.10
+               src/Makefiles/Makefile.IRIX.5.x
+               src/Makefiles/Makefile.IRIX64
+               src/Makefiles/Makefile.ISC
+               src/Makefiles/Makefile.KSR
+               src/Makefiles/Makefile.NEWS-OS.4.x
+               src/Makefiles/Makefile.NEWS-OS.6.x
+               src/Makefiles/Makefile.NEXTSTEP
+               src/Makefiles/Makefile.NonStop-UX
+               src/Makefiles/Makefile.Paragon
+               src/Makefiles/Makefile.SCO.3.2v4.2
+               src/Makefiles/Makefile.SunOS.5.3
+               src/Makefiles/Makefile.SunOS.5.4
+               src/Makefiles/Makefile.SunOS.5.5
+               src/Makefiles/Makefile.UNIX_SV.4.x.i386
+               src/Makefiles/Makefile.uts.systemV
+               src/Makefiles/Makefile.UX4800
+               src/aliases.0
+               src/mailq.0
+               src/mime.c
+               src/newaliases.0
+               src/sendmail.0
+               test/t_seteuid.c
+       RENAMED FILES:
+               cf/cf/alpha.mc =>               cf/cf/s2k-osf1.mc
+               cf/cf/chez.mc =>                cf/cf/chez.cs.mc
+               cf/cf/hpux-cs-exposed.mc =>     cf/cf/cs-hpux9.mc
+               cf/cf/osf1-cs-exposed.mc =>     cf/cf/cs-osf1.mc
+               cf/cf/s2k.mc =>                 cf/cf/s2k-ultrix4.mc
+               cf/cf/sunos4.1-cs-exposed.mc => cf/cf/cs-sunos4.1.mc
+               cf/cf/ultrix4.1-cs-exposed.mc => cf/cf/cs-ultrix4.mc
+               cf/cf/vangogh.mc =>             cf/cf/vangogh.cs.mc
+               cf/domain/Berkeley.m4 =>        cf/domain/Berkeley.EDU.m4
+               cf/domain/cs-exposed.m4 =>      cf/domain/CS.Berkeley.EDU.m4
+               cf/domain/eecs-hidden.m4 =>     cf/domain/EECS.Berkeley.EDU.m4
+               cf/domain/s2k.m4 =>             cf/domain/S2K.Berkeley.EDU.m4
+               cf/ostype/hpux.m4 =>            cf/ostype/hpux9.m4
+               cf/ostype/irix.m4 =>            cf/ostype/irix4.m4
+               cf/ostype/ultrix4.1.m4 =>       cf/ostype/ultrix4.m4
+               src/Makefile.* =>               src/Makefiles/Makefile.*
+               src/Makefile.AUX =>             src/Makefiles/Makefile.A-UX
+               src/Makefile.BSDI =>            src/Makefiles/Makefile.BSD-OS
+               src/Makefile.DGUX =>            src/Makefiles/Makefile.dgux
+               src/Makefile.RISCos =>          src/Makefiles/Makefile.UMIPS
+               src/Makefile.SunOS.4.0.3 =>     src/Makefiles/Makefile.SunOS.4.0
+       OBSOLETED FILES:
+               cf/cf/cogsci.mc
+               cf/cf/cs-exposed.mc
+               cf/cf/cs-hidden.mc
+               cf/cf/hpux-cs-hidden.mc
+               cf/cf/knecht.mc
+               cf/cf/osf1-cs-hidden.mc
+               cf/cf/sunos3.5-cs-exposed.mc
+               cf/cf/sunos3.5-cs-hidden.mc
+               cf/cf/sunos4.1-cs-hidden.mc
+               cf/cf/ultrix4.1-cs-hidden.mc
+               cf/domain/cs-hidden.m4
+               contrib/rcpt-streaming
+               src/Makefiles/Makefile.SunOS.5.x
+
 8.6.12/8.6.12  95/03/28
        Fix to IDENT code (it was getting the size of the reply buffer
                too small, so nothing was ever accepted).  Fix from several
@@ -161,7 +1617,7 @@ summary of the changes in that release.
                and drops core for debugging.  This is an attempt to
                track down a bug that I thought was long since gone.
                If you see this, please forward the log fragment to
-               sendmail@CS.Berkeley.EDU.
+               sendmail@sendmail.ORG.
        Change OLD_NEWDB from a #ifdef to a #if so it can be turned off
                with -DOLD_NEWDB=0 on the command line.  From Christophe
                Wolfhugel.
@@ -206,7 +1662,7 @@ summary of the changes in that release.
                        Newcastle upon Tyne.
                IRIX from Dean Cookson and Bill Driscoll of Mitre
                        Corporation.
-               NCR 3000 from Kevin Darcy of Chrysler Corporation.
+               NCR 3000 from Kevin Darcy of Chrysler Financial Corporation.
                SunOS (it has setsid() and setvbuf() calls) from
                        Jonathan Kamens of OpenVision Technologies.
                HP-UX from Tor Lillqvist.
@@ -244,7 +1700,7 @@ summary of the changes in that release.
                the sender instead of the recipient if the mail was
                from a local user to another local user.  From
                Motonori Nakamura of Kyoto University.
-       Fix "wildcard" on /etc/shell matching -- instead of looking
+       Fix "wildcard" on /etc/shells matching -- instead of looking
                for "*", look for "/SENDMAIL/ANY/SHELL/".  From
                Bryan Costales of ICSI.
        Change the method used to declare the "statfs" availability;
@@ -603,9 +2059,10 @@ summary of the changes in that release.
                included.
        Remember to set $i during queue runs.  Reported by Stephen
                Campbell of Dartmouth University.
-       If ${HOSTALIASES} is set, use it during canonification so that
-               headers are properly mapped.  Reported by Anne Bennett
-               of Concordia University.
+       If the environment variable HOSTALIASES is set, use it during
+               canonification as the name of a file with per-user host
+               translations so that headers are properly mapped.  Reported
+               by Anne Bennett of Concordia University.
        Avoid printing misleading error message if SMTP mailer (not
                using [IPC]) should die on a core dump.
        Avoid incorrect diagnosis of "file 1 closed" when it is caused
@@ -795,82 +2252,6 @@ summary of the changes in that release.
        CONFIG: handle <list:;> syntax correctly.  This isn't legal, but
                it shouldn't fail miserably.  From Motonori Nakamura.
 
-8.6.3/8.6.3    93/10/24
-       IMPORTANT FIX: Fix several problems that caused open files to
-               be "lost" during queue runs; this overflowed the open
-               file table on large runs.  An assumption that fdopen
-               always succeeds sometimes resulted in core dumps when
-               this happens; sometimes the message is delivered twice,
-               sometimes (probably) infinite times.  This problem in
-               various form was reported by P{r (Pell) Emanuelsson and
-               Robert Campbell of U.C. Berkeley.
-       Special diagnosis of EMFILE error conditions -- it now prints
-               the known open file descriptors so you can figure out
-               what is consuming so much resources.
-       Fix a couple of problems caused by early address parsing
-               errors -- one caused it to return a "this is only a
-               warning" when it really wasn't, and the other started
-               parsing through a random pointer.  The first was
-               noted by Eric Wassenaar.
-       Fix an infinite loop problem caused by null components in the
-               host signature.  Problem noted by Jan Sorensen.
-       Be sure to reset the "current date" when sending an error
-               message -- PostMasterCopy messages were being sent
-               with an old Date: header.
-       Fix a problem that caused duplicated mail when sendmail was
-               (1) compiled without HASFLOCK, (2) you are sending to
-               an alias that has an owner-* alias, (3) you execute
-               sendmail with -t flag, (4) you run in -odb mode, and
-               (5) the sender specifies both the alias name and
-               another alias [i.e., the envelope is split], then
-               duplicate messages are sent.  The problem description
-               and one-line fix are from Motonori Nakamura of Kyoto
-               University.
-       Avoid a problem that causes error messages to be discarded
-               in some cases -- this was the result of a "fix" to
-               avoid duplicate error messages, but two are better
-               than zero.  Reported by Tim Rylance.
-       Fix a minor botch in checkfd012() -- fix from Dave Hill of
-               Computervision R&D Ltd.
-       Remove "X-Authentication-Warning: <user> set sender to <address>
-               using -f" entirely -- it is far too eager to include
-               this, and it is confusing folks.  I'll try to make it
-               work "right" in 8.7.  Problem noted by Yoshitaka
-               Tokugawa of dit Co., Ltd.
-       Fix a race condition with the errno value in tick() and
-               reapchild() -- this caused occasional misdiagnosis
-               of problems.  Kyle Jones of UUNET helped this along.
-       Repair rule loop-detection code.  From Michael Corrigan of
-               U.C. San Diego.
-       Fix a problem that caused sender domain addition (C mailer
-               flag to be ignored if you use -odq or use -odb with
-               a high load average.  Problem reported by Jim Murray
-               of Stratus.
-       Fix ident protocol on multi-homed machines.  It was not
-               always using the correct interface.  Fix from J.R.
-               Oldroyd of Opal.
-       Previously, sendmail assumed that any SMTP greeting message
-               that wasn't 2xx was a temporary failure -- it should
-               only take 4xx as a temporary failure, and return a
-               solid error message on anything else -- for example,
-               to allow you to reject connections on a workstation
-               that is MXed to a mail server.
-       Portability enhancements for 386BSD/FreeBSD/NetBSD from
-               Ollivier Robert.
-       CONFIG: FEATURE(always_add_domain) didn't always add the domain;
-               in particular, on local mail it modified the header sender
-               but not the header recipient address(es).  Reported by
-               Jeffrey Honig of Cornell University.  Also, strip
-               any host from envelope recipient address(es), since
-               local mailers don't understand host names -- this is
-               to help mailertable entries.  From Christopher Davis.
-       CONFIG: masquerading didn't apply to addresses that already
-               had a domain.  This change replaces a local hostname
-               by the masquerade name in the SMTP mailer (previously
-               it only added the masquerade name if it didn't already
-               have a domain name).  Several people complained about
-               this.
-
 8.6.2/8.6.2    93/10/15
        Put a "successful delivery" message in the transcript for
                addresses that get return-receipts.
index d205497..3a4979f 100644 (file)
@@ -4,13 +4,12 @@
 
                Eric Allman <eric@CS.Berkeley.EDU>
 
-               @(#)README      8.28 (Berkeley) 4/14/94
+               @(#)README      8.81 (Berkeley) 12/3/95
 
 
 This document describes the sendmail configuration files being used
-at Berkeley.  These use features in the new (R8) sendmail, and although
-there is an ``OLDSENDMAIL'' mode, they haven't really been tested on
-old versions of sendmail and cannot be expected to work well.
+at Berkeley.  These use features in the new (R8) sendmail; they will
+not work on other versions.
 
 These configuration files are probably not as general as previous
 versions, and don't handle as many of the weird cases automagically.
@@ -36,9 +35,9 @@ also works.  Unfortunately, I'm told that the M4 on BSDI 1.0 doesn't
 work -- you'll have to use a Net/2 or GNU version.
 
 IF YOU DON'T HAVE A BERKELEY MAKE, don't despair!  Just run
-"m4 foo.mc > foo.cf" -- that should be all you need.  There is also
-a fairly crude (but functional) Makefile.dist that works on the
-old version of make.
+"m4 ../m4/cf.m4 foo.mc > foo.cf" -- that should be all you need.
+There is also a fairly crude (but functional) Makefile.dist that works
+on the old version of make.
 
 To get started, you may want to look at tcpproto.mc (for TCP-only
 sites), uucpproto.mc (for UUCP-only sites), and clientproto.mc (for
@@ -65,41 +64,73 @@ to great effect.  But it should get you started.
 
 Configuration files are contained in the subdirectory "cf", with a
 suffix ".mc".  They must be run through "m4" to produce a ".cf" file.
+You must pre-load "cf.m4":
 
-Let's examine a typical .mc file (cf/cs-exposed.mc):
+       m4 ${CFDIR}/m4/cf.m4 config.mc > config.cf
+
+where ${CFDIR} is the root of the cf directory and config.mc is the
+name of your configuration file.  If you are running a version of M4
+that understands the __file__ builtin (versions of GNU m4 >= 0.75 do
+this, but the versions distributed with 4.4BSD and derivatives do not)
+or the -I flag (ditto), then ${CFDIR} can be in an arbitrary directory.
+For "traditional" versions, ${CFDIR} ***MUST*** be "..", or you MUST
+use -D_CF_DIR_=/path/to/cf/dir/ -- note the trailing slash!  For example:
+
+       m4 -D_CF_DIR_=${CFDIR}/ ${CFDIR}/m4/cf.m4 config.mc > config.cf
+
+Let's examine a typical .mc file:
 
        divert(-1)
        #
        # Copyright (c) 1983 Eric P. Allman
-       # Copyright (c) 1988 The Regents of the University of California.
-       # All rights reserved.
+       # Copyright (c) 1988, 1993
+       #       The Regents of the University of California.  All rights reserved.
        #
-       # Redistribution and use in source and binary forms are permitted
-       # provided that the above copyright notice and this paragraph are
-       # duplicated in all such forms and that any documentation,
-       # advertising materials, and other materials related to such
-       # distribution and use acknowledge that the software was developed
-       # by the University of California, Berkeley.  The name of the
-       # University may not be used to endorse or promote products derived
-       # from this software without specific prior written permission.
-       # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-       # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-       # WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+       # 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.
        #
 
-The divert(-1) will delete the crud in the resulting output file.
-The copyright notice is what your lawyers require.  Our lawyers require
-the one that I've included in my files. A copyleft is a copyright by
-another name.
-
-The next line MUST be
-
-       include(`../m4/cf.m4')
+       #
+       #  This is a Berkeley-specific configuration file for HP-UX 9.x.
+       #  It applies only the the Computer Science Division at Berkeley,
+       #  and should not be used elsewhere.   It is provided on the sendmail
+       #  distribution as a sample only.  To create your own configuration
+       #  file, create an appropriate domain file in ../domain, change the
+       #  `DOMAIN' macro below to reference that file, and copy the result
+       #  to a name of your own choosing.
+       #
+       divert(0)
 
-This will pull in the M4 macros you will need to make sense of
-everything else.  As the saying goes, don't think about it, just
-do it.  If you don't do it, don't bother reading the rest of this
-file.
+The divert(-1) will delete the crud in the resulting output file.
+The copyright notice can be replace by whatever your lawyers require;
+our lawyers require the one that I've included in my files.  A copyleft
+is a copyright by another name.  The divert(0) restores regular output.
 
        VERSIONID(`<SCCS or RCS version id>')
 
@@ -108,40 +139,67 @@ resulting file.  We use SCCS; you could use RCS, something else, or
 omit it completely.  This is not the same as the version id included
 in SMTP greeting messages -- this is defined in m4/version.m4.
 
-       DOMAIN(cs.exposed)
+       OSTYPE(hpux9)dnl
+
+You must specify an OSTYPE to properly configure things such as the
+pathname of the help and status files, the flags needed for the local
+mailer, and other important things.  If you omit it, you will get an
+error when you try to build the configuration.  Look at the ostype
+directory for the list of known operating system types.
+
+       DOMAIN(CS.Berkeley.EDU)dnl
 
-This example exposes the host inside of the CS subdomain -- that is,
-it doesn't try to hide the name of the workstation to the outside
-world.  Changing this to DOMAIN(cs.hidden) would have made outgoing
-messages refer to "<username>@CS.Berkeley.EDU" instead of using the
-local hostname.  Internally this is effected by using
-"MASQUERADE_AS(CS.Berkeley.EDU)".
+This example is specific to the Computer Science Division at Berkeley.
+You can use "DOMAIN(generic)" to get a sufficiently bland definition
+that may well work for you, or you can create a customized domain
+definition appropriate for your environment.
 
+       MAILER(local)
        MAILER(smtp)
 
 These describe the mailers used at the default CS site site.  The
-local mailer is always included automatically.
+local mailer is always included automatically.  Beware MAILER
+declarations should always be last in the configuration file, and
+MAILER(smtp) should always precede MAILER(uucp).
+
+One word of warning:  M4 macros are expanded even in lines that appear
+to be comments.  For example, if you have
+
+       # See FEATURE(foo) above
+
+it will not do what you expect, because the FEATURE(foo) will be
+expanded.  This also applies to
+
+       # And then define the $X macro to be the return address
+
+because ``define'' is an M4 keyword.  If you want to use them, surround
+them with directed quotes, `like this'.
 
 
 +--------+
 | OSTYPE |
 +--------+
 
-Note that cf/cs-exposed.mc omits an OSTYPE macro -- this assumes
-default Computer Science Division environment.  There are several
-explicit environments available: bsd4.3, bsd4.4, hpux, irix, osf1,
-riscos4.5, sunos3.5, sunos4.1, and ultrix4.1.  These change things
-like the location of the alias file and queue directory.  Some of
-these files are identical to one another.
+You MUST define an operating system environment, or the configuration
+file build will puke.  There are several environments available; look
+at the "ostype" directory for the current list.  This macro changes
+things like the location of the alias file and queue directory.  Some
+of these files are identical to one another.
+
+It is IMPERATIVE that the OSTYPE occur before any MAILER definitions.
+In general, the OSTYPE macro should go immediately after any version
+information, and MAILER definitions should always go last.
 
-Operating system definitions are easy to write.  They may define
-the following variables (everything defaults, so an ostype file
-may be empty).
+Operating system definitions are usually easy to write.  They may define
+the following variables (everything defaults, so an ostype file may be
+empty).  Unfortunately, the list of configuration-supported systems is
+not as broad as the list of source-supported systems, since many of
+the source contributors do not include corresponding ostype files.
 
 ALIAS_FILE             [/etc/aliases] The location of the text version
                        of the alias file(s).  It can be a comma-separated
                        list of names (but be sure you quote values with
-                       comments in them -- for example, use
+                       commas in them -- for example, use
                                define(`ALIAS_FILE', `a,b')
                        to get "a" and "b" both listed as alias files;
                        otherwise the define() primitive only sees "a").
@@ -157,11 +215,19 @@ LOCAL_MAILER_FLAGS        [rmn] The flags used by the local mailer.  The
                        flags lsDFM are always included.
 LOCAL_MAILER_ARGS      [mail -d $u] The arguments passed to deliver local
                        mail.
+LOCAL_MAILER_MAX       [undefined] If defined, the maximum size of local
+                       mail that you are willing to accept.
+LOCAL_MAILER_CHARSET   [undefined] If defined, messages containing 8-bit data
+                       that ARRIVE from an address that resolves to the
+                       local mailer and which are converted to MIME will be
+                       labelled with this character set.
 LOCAL_SHELL_PATH       [/bin/sh] The shell used to deliver piped email.
 LOCAL_SHELL_FLAGS      [eu] The flags used by the shell mailer.  The
                        flags lsDFM are always included.
 LOCAL_SHELL_ARGS       [sh -c $u] The arguments passed to deliver "prog"
                        mail.
+LOCAL_SHELL_DIR                [$z:/] The directory search path in which the
+                       shell should run.
 USENET_MAILER_PATH     [/usr/lib/news/inews] The name of the program
                        used to submit news.
 USENET_MAILER_FLAGS    [rlsDFMmn] The mailer flags for the usenet mailer.
@@ -170,20 +236,66 @@ USENET_MAILER_ARGS        [-m -h -n] The command line arguments for the
 USENET_MAILER_MAX      [100000] The maximum size of messages that will
                        be accepted by the usenet mailer.
 SMTP_MAILER_FLAGS      [undefined] Flags added to SMTP mailer.  Default
-                       flags are `mDFMUX' (and `a' for esmtp mailer).
+                       flags are `mDFMUX' for all SMTP-based mailers; the
+                       "esmtp" mailer adds `a' and "smtp8" adds `8'.
 SMTP_MAILER_MAX                [undefined] The maximum size of messages that will
-                       be transported using the smtp or esmtp mailers.
+                       be transported using the smtp, smtp8, or esmtp
+                       mailers.
+SMTP_MAILER_ARGS       [IPC $h] The arguments passed to the smtp mailer.
+                       About the only reason you would want to change this
+                       would be to change the default port.
+ESMTP_MAILER_ARGS      [IPC $h] The arguments passed to the esmtp mailer.
+SMTP8_MAILER_ARGS      [IPC $h] The arguments passed to the smtp8 mailer.
+RELAY_MAILER_ARGS      [IPC $h] The arguments passed to the relay mailer.
+SMTP_MAILER_CHARSET    [undefined] If defined, messages containing 8-bit data
+                       that ARRIVE from an address that resolves to one of
+                       the SMTP mailers and which are converted to MIME will
+                       be labelled with this character set.
 UUCP_MAILER_FLAGS      [undefined] Flags added to UUCP mailer.  Default
-                       flags are `DFMhuU' (and `m' for suucp mailer, minus
-                       `U' for uucp-dom mailer).
-UUCP_MAILER_ARGS       [uux - -r -z -a$f -gC $h!rmail ($u)] The arguments
+                       flags are `DFMhuU' (and `m' for uucp-new mailer,
+                       minus `U' for uucp-dom mailer).
+UUCP_MAILER_ARGS       [uux - -r -z -a$g -gC $h!rmail ($u)] The arguments
                        passed to the UUCP mailer.
-UUCP_MAX_SIZE          [100000] The maximum size message accepted for
+UUCP_MAILER_MAX                [100000] The maximum size message accepted for
                        transmission by the UUCP mailers.
+UUCP_MAILER_CHARSET    [undefined] If defined, messages containing 8-bit data
+                       that ARRIVE from an address that resolves to one of
+                       the UUCP mailers and which are converted to MIME will
+                       be labelled with this character set.
 FAX_MAILER_PATH                [/usr/local/lib/fax/mailfax] The program used to
                        submit FAX messages.
 FAX_MAILER_MAX         [100000] The maximum size message accepted for
                        transmission by FAX.
+POP_MAILER_PATH                [/usr/lib/mh/spop] The pathname of the POP mailer.
+POP_MAILER_FLAGS       [Penu] Flags added to POP mailer.  Flags "lsDFM"
+                       are always added.
+POP_MAILER_ARGS                [pop $u] The arguments passed to the POP mailer.
+PROCMAIL_MAILER_FLAGS  [Shu] Flags added to Procmail mailer.  Flags
+                       ``DFMmn'' are always set.
+PROCMAIL_MAILER_ARGS   [procmail -m $h $f $u] The arguments passed to
+                       the Procmail mailer.
+PROCMAIL_MAILER_MAX    [undefined] If set, the maximum size message that
+                       will be accepted by the procmail mailer.
+MAIL11_MAILER_PATH     [/usr/etc/mail11] The path to the mail11 mailer.
+MAIL11_MAILER_FLAGS    [nsFx] Flags for the mail11 mailer.
+MAIL11_MAILER_ARGS     [mail11 $g $x $h $u] Arguments passed to the mail11
+                       mailer.
+PH_MAILER_PATH         [/usr/local/etc/phquery] The path to the phquery
+                       program.
+PH_MAILER_FLAGS                [ehmu] Flags for the phquery mailer.
+PH_MAILER_ARGS         [phquery -- $u] -- arguments to the phquery mailer.
+CYRUS_MAILER_FLAGS     [A5@] The flags used by the cyrus mailer.  The
+                       flags lsDFMnP are always included.
+CYRUS_MAILER_PATH      [/usr/cyrus/bin/deliver] The progam used to deliver
+                       cyrus mail.
+CYRUS_MAILER_ARGS      [deliver -e -m $h -- $u] The arguments passed
+                       to deliver cyrus mail.
+CYRUS_BB_MAILER_FLAGS  [undefined] The flags used by the cyrusbb
+                       mailer. The flags lsDFMnP are always included.
+CYRUS_BB_MAILER_ARGS   [deliver -e -m $u] The arguments passed
+                       to deliver cyrusbb mail.
+
+
 
 +---------+
 | DOMAINS |
@@ -194,21 +306,28 @@ file, referenced by the DOMAIN macro.  For example, our Berkeley
 domain file includes definitions for several internal distinguished
 hosts:
 
-UUCP_RELAY     The host that will forward UUCP-addressed email.
+UUCP_RELAY     The host that will accept UUCP-addressed email.
                If not defined, all UUCP sites must be directly
                connected.
-BITNET_RELAY   The host that will forward BITNET-addressed email.
+BITNET_RELAY   The host that will accept BITNET-addressed email.
                If not defined, the .BITNET pseudo-domain won't work.
-LOCAL_RELAY    The site that will handle unqualified names -- that
-               is, names with out an @domain extension.  If not set,
-               they are assumed to belong on this machine.  This
-               allows you to have a central site to store a
+DECNET_RELAY   The host that will accept DECNET-addressed email.
+               If not defined, the .DECNET pseudo-domain and addresses
+               of the form node::user will not work.
+FAX_RELAY      The host that will accept mail to the .FAX pseudo-domain.
+               The "fax" mailer overrides this value.
+LOCAL_RELAY    DEPRECATED.  The site that will handle unqualified
+               names -- that is, names with out an @domain extension.
+               If not set, they are assumed to belong on this machine.
+               This allows you to have a central site to store a
                company- or department-wide alias database.  This
-               only works at small sites, and there are better
-               methods.
+               only works at small sites, and only with some user
+               agents.
+LUSER_RELAY    The site that will handle lusers -- that is, apparently
+               local names that aren't local accounts or aliases.
 
-Each of these can be either ``mailer:hostname'' (in which case the
-mailer is the internal mailer name, such as ``suucp'' and the hostname
+Any of these can be either ``mailer:hostname'' (in which case the
+mailer is the internal mailer name, such as ``uucp-new'' and the hostname
 is the name of the host as appropriate for that mailer) or just a
 ``hostname'', in which case a default mailer type (usually ``relay'',
 a variant on SMTP) is used.  WARNING: if you have a wildcard MX
@@ -231,7 +350,11 @@ knowledge" into one place.
 +---------+
 
 There are fewer mailers supported in this version than the previous
-version, owing mostly to a simpler world.
+version, owing mostly to a simpler world.  As a general rule, put the
+MAILER definitions last in your .mc file, and always put MAILER(smtp)
+before MAILER(uucp) -- several features and definitions will modify
+the definition of mailers, and the smtp mailer modifies the UUCP
+mailer.
 
 local          The local and prog mailers.  You will almost always
                need these; the only exception is if you relay ALL
@@ -242,23 +365,29 @@ smtp              The Simple Mail Transport Protocol mailer.  This does
                not hide hosts behind a gateway or another other
                such hack; it assumes a world where everyone is
                running the name server.  This file actually defines
-               three mailers: "smtp" for regular (old-style) SMTP to
+               four mailers: "smtp" for regular (old-style) SMTP to
                other servers, "esmtp" for extended SMTP to other
-               servers, and "relay" for transmission to our
-               RELAY_HOST or MAILER_HUB.
+               servers, "smtp8" to do SMTP to other servers without
+               converting 8-bit data to MIME (essentially, this is
+               your statement that you know the other end is 8-bit
+               clean even if it doesn't say so), and "relay" for
+               transmission to our RELAY_HOST, LUSER_RELAY, or
+               MAILER_HUB.
 
 uucp           The Unix-to-Unix Copy Program mailer.  Actually, this
-               defines two mailers, "uucp" and "suucp".  The latter
-               is for when you know that the UUCP mailer at the other
-               end can handle multiple recipients in one transfer.
-               When you invoke this, sendmail looks for all names in
+               defines two mailers, "uucp-old" (a.k.a. "uucp") and
+               "uucp-new" (a.k.a. "suucp").  The latter is for when you
+               know that the UUCP mailer at the other end can handle
+               multiple recipients in one transfer.  If the smtp mailer
+               is also included in your configuration, two other mailers
+               ("uucp-dom" and "uucp-uudom") are also defined [warning:
+               you MUST specify MAILER(smtp) before MAILER(uucp)].  When you
+               include the uucp mailer, sendmail looks for all names in
                the $=U class and sends them to the uucp-old mailer; all
                names in the $=Y class are sent to uucp-new; and all
                names in the $=Z class are sent to uucp-uudom.  Note that
                this is a function of what version of rmail runs on
                the receiving end, and hence may be out of your control.
-               If smtp is defined, it also defines "uucp-dom" and
-               "uucp-uudom" mailers that use domain-style rewriting.
                See the section below describing UUCP mailers in more
                detail.
 
@@ -274,6 +403,51 @@ fax                Facsimile transmission.  This is experimental and based
 
 pop            Post Office Protocol.
 
+procmail       An interface to procmail (does not come with sendmail).
+               This is designed to be used in mailertables.  For example,
+               a common question is "how do I forward all mail for a given
+               domain to a single person?".  If you have this mailer
+               defined, you could set up a mailertable reading:
+
+                       host.com        procmail:/etc/procmailrcs/host.com
+
+               with the file /etc/procmailrcs/host.com reading:
+
+                       :0      # forward mail for host.com
+                       ! -oi -f $1 person@other.host
+
+               This would arrange for (anything)@host.com to be sent
+               to person@other.host.  Within the procmail script, $1 is
+               the name of the sender and $2 is the name of the recipient.
+               If you use this with FEATURE(local_procmail), the FEATURE
+               should be listed first.
+
+mail11         The DECnet mail11 mailer, useful only if you have the mail11
+               program from gatekeeper.dec.com:/pub/DEC/gwtools (and
+               DECnet, of course).  This is for Phase IV DECnet support;
+               if you have Phase V at your site you may have additional
+               problems.
+
+phquery                The phquery program.  This is somewhat counterintuitively
+               referenced as the "ph" mailer internally.  It can be used
+               to do CCSO name server lookups.  The phquery program, which
+               this mailer uses, is distributed with the ph client.
+
+cyrus          The cyrus and cyrusbb mailers.  The cyrus mailer delivers to
+               a local cyrus user.  this mailer can make use of the
+               "user+detail@local.host" syntax; it will deliver the mail to
+               the user's "detail" mailbox if the mailbox's ACL permits.
+               The cyrusbb mailer delivers to a system-wide cyrus mailbox
+               if the mailbox's ACL permits.
+
+
+The local mailer accepts addresses of the form "user+detail", where
+the "+detail" is not used for mailbox matching but is available
+to certain local mail programs (in particular, see FEATURE(local_procmail)).
+For example, "eric", "eric+sendmail", and "eric+sww" all indicate
+the same user, but additional arguments <null>, "sendmail", and "sww"
+may be provided for use in sorting mail.
+
 
 +----------+
 | FEATURES |
@@ -300,6 +474,13 @@ use_cw_file        Read the file /etc/sendmail.cw file to get alternate
                The actual filename can be overridden by redefining
                confCW_FILE.
 
+use_ct_file    Read the file /etc/sendmail.ct file to get the names
+               of users that will be ``trusted'', that is, able to
+               set their envelope from address using -f without
+               generating a warning message.
+               The actual filename can be overridden by redefining
+               confCT_FILE.
+
 redirect       Reject all mail addressed to "address.REDIRECT" with
                a ``551 User not local; please try <address>'' message.
                If this is set, you can alias people who have left
@@ -315,11 +496,14 @@ nocanonify        Don't pass addresses to $[ ... $] for canonification.
                turn off the usual resolver options that do a similar
                thing.
 
-notsticky      By default, email sent to "user@local.host" are marked
+stickyhost     If set, email sent to "user@local.host" are marked
                as "sticky" -- that is, the local addresses aren't
                matched against UDB and don't go through ruleset 5.
-               This features disables this treatment.  It would
-               normally be used on network gateway machines.
+               This is used if you want a set up where "user" is
+               not necessarily the same as "user@local.host", e.g.,
+               to make a distinct domain-wide namespace.  Prior to
+               8.7 this was the default, and notsticky was used to
+               turn this off.
 
 mailertable    Include a "mailer table" which can be used to override
                routing for particular domains.  The argument of the
@@ -336,12 +520,15 @@ mailertable       Include a "mailer table" which can be used to override
                reflected into the message header.
 
 domaintable    Include a "domain table" which can be used to provide
-               full domains on unqualified (single word) hosts.  The
-               argument of the FEATURE may be the key definition.  If
-               none is specified, the definition used is:
+               domain name mapping.  Use of this should really be
+               limited to your own domains.  It may be useful if you
+               change names (e.g., your company changes names from
+               oldname.com to newname.com).  The argument of the
+               FEATURE may be the key definition.  If none is specified,
+               the definition used is:
                        hash -o /etc/domaintable
-               The key in this table is the unqualified host name; the
-               value is the fully qualified domain.  Anything in the
+               The key in this table is the domain name; the value is
+               the new (fully qualified) domain.  Anything in the
                domaintable is reflected into headers; that is, this
                is done in ruleset 3.
 
@@ -395,6 +582,29 @@ nullclient This is a special case -- it creates a stripped down
                defaults to the name of the hub machine).  No mailers
                should be defined.  No aliasing or forwarding is done.
 
+local_procmail Use procmail as the local mailer.  This mailer can
+               make use of the "user+indicator@local.host" syntax;
+               normally the +indicator is just tossed, but by default
+               it is passed as the -a argument to procmail.  The
+               argument to this feature is the pathname of procmail,
+               which defaults to /usr/local/bin/procmail.
+
+bestmx_is_local        Accept mail as though locally addressed for any host that
+               lists us as the best possible MX record.  This generates
+               additional DNS traffic, but should be OK for low to
+               medium traffic hosts.  THIS FEATURE IS FUNDAMENTALLY
+               INCOMPATIBLE WITH WILDCARD MX RECORDS!!!  If you have
+               a wildcard MX record that matches your domain, you
+               cannot use this feature.
+
+smrsh          Use the SendMail Restricted SHell (smrsh) provided
+               with the distribution instead of /bin/sh for mailing
+               to programs.  This improves the ability of the local
+               system administrator to control what gets run via
+               e-mail.  If an argument is provided it is used as the
+               pathname to smrsh; otherwise, /usr/local/etc/smrsh is
+               assumed.
+
 
 +-------+
 | HACKS |
@@ -413,6 +623,14 @@ subdomains.
 | SITE CONFIGURATION |
 +--------------------+
 
+    *****************************************************
+    * This section is really obsolete, and is preserved        *
+    * only for back compatibility.  You should plan on *
+    * using mailertables for new installations.          In    *
+    * particular, it doesn't work for the newer forms  *
+    * of UUCP mailers, such as uucp-uudom.             *
+    *****************************************************
+
 Complex sites will need more local configuration information, such as
 lists of UUCP hosts they speak with directly.  This can get a bit more
 tricky.  For an example of a "complex" site, see cf/ucbvax.mc.
@@ -512,7 +730,8 @@ The four mailers are:
 
     uucp-dom
        This UUCP mailer keeps everything as domain addresses.
-       Basically, it uses the SMTP mailer rewriting rules.
+       Basically, it uses the SMTP mailer rewriting rules.  This mailer
+       is only included if MAILER(smtp) is also specified.
 
        Unfortunately, a lot of UUCP mailer transport agents require
        bangified addresses in the envelope, although you can use
@@ -526,7 +745,8 @@ The four mailers are:
        local hostname, unless there is no host name on the address
        at all (e.g., "wolf") or the host component is a UUCP host name
        instead of a domain name ("somehost!wolf" instead of
-       "some.dom.ain!wolf").
+       "some.dom.ain!wolf").  This is also included only if MAILER(smtp)
+       is also specified.
 
 Examples:
 
@@ -623,7 +843,29 @@ The masquerade name is not normally canonified, so it is important
 that it be your One True Name, that is, fully qualified and not a
 CNAME.
 
-there are always users that need to be "exposed" -- that is, their
+Normally the only addresses that are masqueraded are those that come
+from this host (that is, are either unqualified or in $=w, the list
+of local domain names).  You can augment this list using
+
+       MASQUERADE_DOMAIN(otherhost.domain)
+
+The effect of this is that although mail to user@otherhost.domain
+will not be delivered locally, any mail including any user@otherhost.domain
+will, when relayed, be rewritten to have the MASQUERADE_AS address.
+This can be a space-separated list of names.
+
+If these names are in a file, you can use
+
+       MASQUERADE_DOMAIN_FILE(filename)
+
+to read the list of names from the indicated file.
+
+Normally only header addresses are masqueraded.  If you want to
+masquerade the envelope as well, use
+
+       FEATURE(masquerade_envelope)
+
+There are always users that need to be "exposed" -- that is, their
 internal site name should be displayed instead of the masquerade name.
 Root is an example.  You can add users to this list using
 
@@ -657,23 +899,28 @@ shared /var/spool/mail scheme, use
        define(`MAIL_HUB', mailer:hostname)
 
 Again, ``mailer:'' defaults to "smtp".  If you define both LOCAL_RELAY
-and MAIL_HUB, unqualified names will be sent to the LOCAL_RELAY and
-other local names will be sent to MAIL_HUB.  Names in $=L will be
-delivered locally, so you MUST have aliases or .forward files for them.
+and MAIL_HUB _AND_ you have FEATURE(stickyhost), unqualified names will
+be sent to the LOCAL_RELAY and other local names will be sent to MAIL_HUB.
+Names in $=L will be delivered locally, so you MUST have aliases or
+.forward files for them.
 
-For example, if are on machine mastodon.CS.Berkeley.EDU, the following
-combinations of settings will have the indicated effects:
+For example, if are on machine mastodon.CS.Berkeley.EDU and you have
+FEATURE(stickyhost), the following combinations of settings will have the
+indicated effects:
 
 email sent to....      eric                      eric@mastodon.CS.Berkeley.EDU
 
 LOCAL_RELAY set to     mail.CS.Berkeley.EDU      (delivered locally)
-mail.CS.Berkeley.EDU
+mail.CS.Berkeley.EDU     (no local aliasing)       (aliasing done)
 
 MAIL_HUB set to                mammoth.CS.Berkeley.EDU   mammoth.CS.Berkeley.EDU
-mammoth.CS.Berkeley.EDU
+mammoth.CS.Berkeley.EDU          (aliasing done)           (aliasing done)
 
 Both LOCAL_RELAY and   mail.CS.Berkeley.EDU      mammoth.CS.Berkeley.EDU
-MAIL_HUB set as above
+MAIL_HUB set as above    (no local aliasing)       (aliasing done)
+
+If you do not have FEATURE(stickyhost) set, then LOCAL_RELAY and
+MAIL_HUB act identically, with MAIL_HUB taking precedence.
 
 If you want all outgoing mail to go to a central relay site, define
 SMART_HOST as well.  Briefly:
@@ -683,11 +930,11 @@ SMART_HOST as well.  Briefly:
                local host (e.g., "eric@mastodon.CS.Berkeley.EDU").
        SMART_HOST applies to names qualified with other hosts.
 
-However, beware that other relays (e.g., UUCP_RELAY, BITNET_RELAY, and
-FAX_RELAY) take precedence over SMART_HOST, so if you really want
-absolutely everything to go to a single central site you will need to
-unset all the other relays -- or better yet, find or build a minimal
-config file that does this.
+However, beware that other relays (e.g., UUCP_RELAY, BITNET_RELAY,
+DECNET_RELAY, and FAX_RELAY) take precedence over SMART_HOST, so if you
+really want absolutely everything to go to a single central site you will
+need to unset all the other relays -- or better yet, find or build a
+minimal config file that does this.
 
 
 +-------------------------------+
@@ -788,6 +1035,21 @@ the host name.  For example, the first line above sends everything
 addressed to "anything.my.domain" to that same host name, but using
 the (presumably experimental) xnet mailer.
 
+In some cases you may want to temporarily turn off MX records,
+particularly on gateways.  For example, you may want to MX
+everything in a domain to one machine that then forwards it
+directly.  To do this, you might use the DNS configuration:
+
+       *.domain.       IN      MX      0       relay.machine
+
+and on relay.machine use the mailertable:
+
+       .domain         smtp:[gateway.domain]
+
+The [square brackets] turn off MX records for this host only.
+If you didn't do this, the mailertable would use the MX record
+again, which would give you an MX loop.
+
 
 +--------------------------------+
 | USING USERDB TO MAP FULL NAMES |
@@ -801,13 +1063,82 @@ is fairly easy.)  The intent was to locate the default maildrop at
 a site, but allow you to override this by sending to a specific host.
 
 If you decide to set up the user database in this fashion, it is
-imperative that you also specify FEATURE(notsticky) -- otherwise,
+imperative that you not use FEATURE(stickyhost) -- otherwise,
 e-mail sent to Full.Name@local.host.name will be rejected.
 
-To build the internal form of the user databae, use:
+To build the internal form of the user database, use:
 
        makemap btree /usr/data/base.db < /usr/data/base.txt
 
+As a general rule, I am adamantly opposed to using full names as
+e-mail addresses, since they are not in any sense unique.  For example,
+the Unix software-development community has two Andy Tannenbaums,
+at least two well-known Peter Deutsches, and at one time Bell Labs
+had two Stephen R. Bournes with offices along the same hallway.
+Which one will be forced to suffer the indignity of being
+Stephen_R_Bourne_2?  The less famous of the two, or the one that
+was hired later?
+
+Finger should handle full names (and be fuzzy).  Mail should use
+handles, and not be fuzzy.  [Not that I expect anyone to pay any
+attention to my opinions.]
+
+
++--------------------------------+
+| MISCELLANEOUS SPECIAL FEATURES |
++--------------------------------+
+
+Plussed users
+       Sometimes it is convenient to merge configuration on a
+       centralized mail machine, for example, to forward all
+       root mail to a mail server.  In this case it might be
+       useful to be able to treat the root addresses as a class
+       of addresses with subtle differences.  You can do this
+       using plussed users.  For example, a client might include
+       the alias:
+
+               root:  root+client1@server
+
+       On the server, this will match an alias for "root+client1".
+       If that is not found, the alias "root+*" will be tried,
+       then "root".
+
+
++----------------+
+| SECURITY NOTES |
++----------------+
+
+A lot of sendmail security comes down to you.  Sendmail 8 is much
+more careful about checking for security problems than previous
+versions, but there are some things that you still need to watch
+for.  In particular:
+
+* Make sure the aliases file isn't writable except by trusted
+  system personnel.  This includes both the text and database
+  version.
+
+* Make sure that other files that sendmail reads, such as the
+  mailertable, is only writable by trusted system personnel.
+
+* The queue directory should not be world writable PARTICULARLY
+  if your system allows "file giveaways" (that is, if a non-root
+  user can chown any file they own to any other user).
+
+* If your system allows file giveaways, DO NOT create a publically
+  writable directory for forward files.  This will allow anyone
+  to steal anyone else's e-mail.  Instead, create a script that
+  copies the .forward file from users' home directories once a
+  night (if you want the non-NFS-mounted forward directory).
+
+* If your system allows file giveaways, you'll find that
+  sendmail is much less trusting of :include: files -- in
+  particular, you'll have to have /SENDMAIL/ANY/SHELL/ in
+  /etc/shells before they will be trusted (that is, before
+  files and programs listed in them will be honored).
+
+In general, file giveaways are a mistake -- if you can turn them
+off I recommend you do so.
+
 
 +------------------+
 | FlexFAX SOFTWARE |
@@ -958,115 +1289,305 @@ be ``double quoted, like this phrase'' to avoid having the comma
 confuse things.  This is common for alias file definitions and for
 the read timeout.
 
-M4 Variable Name       Default         Mac/Opt Description
-================       =======         ======= ===========
-confMAILER_NAME                MAILER-DAEMON   Dn      The sender name used for
-                                               internally generated
-                                               outgoing messages.
-confFROM_LINE          From $g  $d     Dl      The From_ line used when
-                                               sending to files or programs.
-confFROM_HEADER                $?x$x <$g>$|$g$.        The format of an internally
-                                       Dq      generated From: address.
-confOPERATORS          .:%@!^/[]       Do      Address operator characters.
-confSMTP_LOGIN_MSG     $j Sendmail $v/$Z ready at $b
-                                       De      The initial (spontaneous)
-                                               SMTP greeting message.
-confSEVEN_BIT_INPUT    False           O7      Force input to seven bits?
-confALIAS_WAIT         10              Oa      Wait (in minutes) for alias
-                                               file rebuild.
-confMIN_FREE_BLOCKS    4               Ob      Minimum number of free blocks
-                                               on queue filesystem to accept
-                                               SMTP mail.
-confBLANK_SUB          .               OB      Blank (space) substitution
-                                               character.
-confCON_EXPENSIVE      False           Oc      Avoid connecting immediately
-                                               to mailers marked expensive?
-confCHECKPOINT_INTERVAL        10              OC      Checkpoint queue files
-                                               every N recipients.
-confDELIVERY_MODE      background      Od      Default delivery mode.
-confAUTO_REBUILD       False           OD      Automatically rebuild
-                                               alias file if needed.
-confERROR_MODE         (undefined)     Oe      Error message mode.
-confERROR_MESSAGE      (undefined)     OE      Error message header/file.
-confSAVE_FROM_LINES    False           Of      Save extra leading
-                                               From_ lines.
-confTEMP_FILE_MODE     0600            OF      Temporary file mode.
-confDEF_GROUP_ID       1               Og      Default group id.
-confMATCH_GECOS                False           OG      Match GECOS field.
-confMAX_HOP            17              Oh      Maximum hop count.
-confIGNORE_DOTS                False           Oi *    Ignore dot as terminator
-                                               for incoming messages?
-confBIND_OPTS          (empty)         OI      Default options for BIND.
-confMIME_FORMAT_ERRORS True            Oj *    Send error messages as MIME-
-                                               encapsulated messages per
-                                               RFC 1344.
-confFORWARD_PATH       (undefined)     OJ      The colon-separated list of
-                                               places to search for .forward
-                                               files.
-confMCI_CACHE_SIZE     2               Ok      Size of open connection cache.
-confMCI_CACHE_TIMEOUT  5m              OK      Open connection cache timeout.
-confUSE_ERRORS_TO      False           Ol *    Use the Errors-To: header to
-                                               deliver error messages.  This
-                                               should not be necessary because
-                                               of general acceptance of the
-                                               envelope/header distinction.
-confLOG_LEVEL          9               OL      Log level.
-confME_TOO             False           Om      Include sender in group
-                                               expansions.
-confCHECK_ALIASES      True            On      Check RHS of aliases when
-                                               running newaliases.
-confOLD_STYLE_HEADERS  True            Oo *    Assume that headers without
-                                               special chars are old style.
-confDAEMON_OPTIONS     (undefined)     OO      SMTP daemon options.
-confPRIVACY_FLAGS      authwarnings    Op      Privacy flags.
-confCOPY_ERRORS_TO     (undefined)     OP      Address for additional copies
-                                               of all error messages.
-confQUEUE_FACTOR       (undefined)     Oq      Slope of queue-only function
-confREAD_TIMEOUT       (undefined)     Or      SMTP read timeouts.
-confSAFE_QUEUE         True            Os *    Commit all messages to disk
-                                               before forking.
-confMESSAGE_TIMEOUT    5d/4h           OT      Timeout for messages before
-                                               sending error/warning message.
-confTIME_ZONE          USE_SYSTEM      Ot      Time zone info -- can be
-                                               USE_SYSTEM to use the system's
-                                               idea, USE_TZ to use the user's
-                                               TZ envariable, or something
-                                               else to force that value.
-confDEF_USER_ID                1               Ou      Default user id.
-confUSERDB_SPEC                (undefined)     OU      User database specification.
-confFALLBACK_MX                (undefined)     OV      Fallback MX host.
-confTRY_NULL_MX_LIST   False           Ow      If we are the best MX for a
-                                               host and haven't made other
-                                               arrangements, try connecting
-                                               to the host directly; normally
-                                               this would be a config error.
-confQUEUE_LA           8               Ox      Load average at which queue-only
-                                               function kicks in.
-confREFUSE_LA          12              OX      Load average at which incoming
-                                               SMTP connections are refused.
+M4 Variable Name       Configuration   Description & [Default]
+================       =============   =======================
+confMAILER_NAME                $n macro        [MAILER-DAEMON] The sender name used
+                                       for internally generated outgoing
+                                       messages.
+confDOMAIN_NAME                $j macro        If defined, sets $j.  This should
+                                       only be done if your system cannot
+                                       determine your local domain name,
+                                       and then it should be set to
+                                       $w.Foo.COM, where Foo.COM is your
+                                       domain name.
+confCF_VERSION         $Z macro        If defined, this is appended to the
+                                       configuration version name.
+confFROM_HEADER                From:           [$?x$x <$g>$|$g$.] The format of an 
+                                       internally generated From: address.
+confRECEIVED_HEADER    Received:
+      [.$?_($?s$|from $.$_) $.by $j ($v/$Z)$?r with $r$. id $i$?u for $u$.; $b]
+                                       The format of the Received: header
+                                       in messages passed through this host.
+                                       It is unwise to try to change this.
+confCW_FILE            Fw class        [/etc/sendmail.cw] Name of file used
+                                       to get the local additions to the $=w
+                                       (local host names) class.
+confCT_FILE            Ft class        [/etc/sendmail.ct] Name of file used
+                                       to get the local additions to the $=t
+                                       (trusted users) class.
+confTRUSTED_USERS      Ct class        [no default] Names of users to add to
+                                       the list of trusted users.  This list
+                                       always includes root, uucp, and daemon.
+                                       See also FEATURE(use_ct_file).
+confSMTP_MAILER                -               [smtp] The mailer name used when
+                                       SMTP connectivity is required.
+                                       One of "smtp", "smtp8", or "esmtp".
+confUUCP_MAILER                -               [uucp-old] The mailer to be used by
+                                       default for bang-format recipient
+                                       addresses.  See also discussion of
+                                       $=U, $=Y, and $=Z in the MAILER(uucp)
+                                       section.
+confLOCAL_MAILER       -               [local] The mailer name used when
+                                       local connectivity is required.
+                                       Almost always "local".
+confRELAY_MAILER       -               [relay] The default mailer name used
+                                       for relaying any mail (e.g., to a
+                                       BITNET_RELAY, a SMART_HOST, or
+                                       whatever).  This can reasonably be
+                                       "uucp-new" if you are on a
+                                       UUCP-connected site.
+confSEVEN_BIT_INPUT    SevenBitInput   [False] Force input to seven bits?
+confEIGHT_BIT_HANDLING EightBitMode    [pass8] 8-bit data handling
+confALIAS_WAIT         AliasWait       [10m] Time to wait for alias file
+                                       rebuild until you get bored and
+                                       decide that the apparently pending
+                                       rebuild failed.
+confMIN_FREE_BLOCKS    MinFreeBlocks   [100] Minimum number of free blocks on
+                                       queue filesystem to accept SMTP mail.
+                                       (Prior to 8.7 this was minfree/maxsize,
+                                       where minfree was the number of free
+                                       blocks and maxsize was the maximum
+                                       message size.  Use confMAX_MESSAGE_SIZE
+                                       for the second value now.)
+confMAX_MESSAGE_SIZE   MaxMessageSize  [infinite] The maximum size of messages
+                                       that will be accepted (in bytes).
+confBLANK_SUB          BlankSub        [ ] Blank (space) substitution
+                                       character.
+confCON_EXPENSIVE      HoldExpensive   [False] Avoid connecting immediately
+                                       to mailers marked expensive?
+confCHECKPOINT_INTERVAL        CheckpointInterval
+                                       [10] Checkpoint queue files every N
+                                       recipients.
+confDELIVERY_MODE      DeliveryMode    [background] Default delivery mode.
+confAUTO_REBUILD       AutoRebuildAliases
+                                       [False] Automatically rebuild alias
+                                       file if needed.
+confERROR_MODE         ErrorMode       [print] Error message mode.
+confERROR_MESSAGE      ErrorHeader     [undefined] Error message header/file.
+confSAVE_FROM_LINES    SafeFromLine    Save extra leading From_ lines.
+confTEMP_FILE_MODE     TempFileMode    [0600] Temporary file mode.
+confMATCH_GECOS                MatchGECOS      [True] Match GECOS field.
+confMAX_HOP            MaxHopCount     [25] Maximum hop count.
+confIGNORE_DOTS*       IgnoreDots      [False; always False in -bs or -bd mode]
+                                       Ignore dot as terminator for incoming
+                                       messages?
+confBIND_OPTS          ResolverOptions [undefined] Default options for DNS
+                                       resolver.
+confMIME_FORMAT_ERRORS*        SendMimeErrors  [True] Send error messages as MIME-
+                                       encapsulated messages per RFC 1344.
+confFORWARD_PATH       ForwardPath     [$z/.forward.$w:$z/.forward]
+                                       The colon-separated list of places to
+                                       search for .forward files.  N.B.: see
+                                       the Security Notes section.
+confMCI_CACHE_SIZE     ConnectionCacheSize
+                                       [2] Size of open connection cache.
+confMCI_CACHE_TIMEOUT  ConnectionCacheTimeout
+                                       [5m] Open connection cache timeout.
+confUSE_ERRORS_TO*     UserErrorsTo    [False] Use the Errors-To: header to
+                                       deliver error messages.  This should
+                                       not be necessary because of general
+                                       acceptance of the envelope/header
+                                       distinction.
+confLOG_LEVEL          LogLevel        [9] Log level.
+confME_TOO             MeToo           [False] Include sender in group
+                                       expansions.
+confCHECK_ALIASES      CheckAliases    [False] Check RHS of aliases when
+                                       running newaliases.  Since this does
+                                       DNS lookups on every address, it can
+                                       slow down the alias rebuild process
+                                       considerably on large alias files.
+confOLD_STYLE_HEADERS* OldStyleHeaders [True] Assume that headers without
+                                       special chars are old style.
+confDAEMON_OPTIONS     DaemonPortOptions
+                                       [none] SMTP daemon options.
+confPRIVACY_FLAGS      PrivacyOptions  [authwarnings] Privacy flags.
+confCOPY_ERRORS_TO     PostmasterCopy  [undefined] Address for additional
+                                       copies of all error messages.
+confQUEUE_FACTOR       QueueFactor     [600000] Slope of queue-only function.
+confDONT_PRUNE_ROUTES  DontPruneRoutes [False] Don't prune down route-addr
+                                       syntax addresses to the minimum
+                                       possible.
+confSAFE_QUEUE*                SuperSafe       [True] Commit all messages to disk
+                                       before forking.
+confTO_INITIAL         Timeout.initial [5m] The timeout waiting for a response
+                                       on the initial connect.
+confTO_HELO            Timeout.helo    [5m] The timeout waiting for a response
+                                       to a HELO or EHLO command.
+confTO_MAIL            Timeout.mail    [10m] The timeout waiting for a
+                                       response to the MAIL command.
+confTO_RCPT            Timeout.rcpt    [1h] The timeout waiting for a response
+                                       to the RCPT command.
+confTO_DATAINIT                Timeout.datainit
+                                       [5m] The timeout waiting for a 354
+                                       response from the DATA command.
+confTO_DATABLOCK       Timeout.datablock
+                                       [1h] The timeout waiting for a block
+                                       during DATA phase.
+confTO_DATAFINAL       Timeout.datafinal
+                                       [1h] The timeout waiting for a response
+                                       to the final "." that terminates a
+                                       message.
+confTO_RSET            Timeout.rset    [5m] The timeout waiting for a response
+                                       to the RSET command.
+confTO_QUIT            Timeout.quit    [2m] The timeout waiting for a response
+                                       to the QUIT command.
+confTO_MISC            Timeout.misc    [2m] The timeout waiting for a response
+                                       to other SMTP commands.
+confTO_COMMAND         Timeout.command [1h] In server SMTP, the timeout waiting
+                                       for a command to be issued.
+confTO_IDENT           Timeout.ident   [30s] The timeout waiting for a response
+                                       to an IDENT query.
+confTO_FILEOPEN                Timeout.fileopen
+                                       [60s] The timeout waiting for a file
+                                       (e.g., :include: file) to be opened.
+confTO_QUEUERETURN     Timeout.queuereturn
+                                       [5d] The timeout before a message is
+                                       returned as undeliverable.
+confTO_QUEUERETURN_NORMAL
+                       Timeout.queuereturn.normal
+                                       [undefined] As above, for normal
+                                       priority messages.
+confTO_QUEUERETURN_URGENT
+                       Timeout.queuereturn.urgent
+                                       [undefined] As above, for urgent
+                                       priority messages.
+confTO_QUEUERETURN_NONURGENT
+                       Timeout.queuereturn.non-urgent
+                                       [undefined] As above, for non-urgent
+                                       (low) priority messages.
+confTO_QUEUEWARN       Timeout.queuewarn
+                                       [4h] The timeout before a warning
+                                       message is sent to the sender telling
+                                       them that the message has been deferred.
+confTO_QUEUEWARN_NORMAL        Timeout.queuewarn.normal
+                                       [undefined] As above, for normal
+                                       priority messages.
+confTO_QUEUEWARN_URGENT        Timeout.queuewarn.urgent
+                                       [undefined] As above, for urgent
+                                       priority messages.
+confTO_QUEUEWARN_NONURGENT
+                       Timeout.queuewarn.non-urgent
+                                       [undefined] As above, for non-urgent
+                                       (low) priority messages.
+confTIME_ZONE          TimeZoneSpec    [USE_SYSTEM] Time zone info -- can be
+                                       USE_SYSTEM to use the system's idea,
+                                       USE_TZ to use the user's TZ envariable,
+                                       or something else to force that value.
+confDEF_USER_ID                DefaultUser     [1:1] Default user id.
+confUSERDB_SPEC                UserDatabaseSpec
+                                       [undefined] User database specification.
+confFALLBACK_MX                FallbackMXhost  [undefined] Fallback MX host.
+confTRY_NULL_MX_LIST   TryNullMXList   [False] If we are the best MX for a
+                                       host and haven't made other
+                                       arrangements, try connecting to the
+                                       host directly; normally this would be
+                                       a config error.
+confQUEUE_LA           QueueLA         [8] Load average at which queue-only
+                                       function kicks in.
+confREFUSE_LA          RefuseLA        [12] Load average at which incoming
+                                       SMTP connections are refused.
 confWORK_RECIPIENT_FACTOR
-                       (undefined)     Oy      Cost of each recipient.
-confSEPARATE_PROC      False           OY      Run all deliveries in a
-                                               separate process.
-confWORK_CLASS_FACTOR  (undefined)     Oz      Priority multiplier for class.
-confWORK_TIME_FACTOR   (undefined)     OZ      Cost of each delivery attempt.
-confCW_FILE            /etc/sendmail.cw        Name of file used to get the
-                                       Fw      local additions to the $=w
-                                               class.
-confSMTP_MAILER                smtp            -       The mailer name used when
-                                               SMTP connectivity is required.
-                                               Either "smtp" or "esmtp".
-confLOCAL_MAILER       local           -       The mailer name used when
-                                               local connectivity is required.
-                                               Almost always "local".
-confRELAY_MAILER       relay           -       The default mailer name used
-                                               for relaying any mail (e.g.,
-                                               to a BITNET_RELAY, a
-                                               SMART_HOST, or whatever).
-                                               This can reasonably be "suucp"
-                                               if you are on a UUCP-connected
-                                               site.
-confDOMAIN_NAME                (undefined)     Dj      If defined, sets $j.
+                       RecipientFactor [30000] Cost of each recipient.
+confSEPARATE_PROC      ForkEachJob     [False] Run all deliveries in a separate
+                                       process.
+confWORK_CLASS_FACTOR  ClassFactor     [1800] Priority multiplier for class.
+confWORK_TIME_FACTOR   RetryFactor     [90000] Cost of each delivery attempt.
+confQUEUE_SORT_ORDER   QueueSortOrder  [Priority] Queue sort algorithm:
+                                       Priority or Host.
+confMIN_QUEUE_AGE      MinQueueAge     [0] The minimum amount of time a job
+                                       must sit in the queue between queue
+                                       runs.  This allows you to set the
+                                       queue run interval low for better
+                                       resposiveness without trying all
+                                       jobs in each run.
+confDEF_CHAR_SET       DefaultCharSet  [unknown-8bit] When converting
+                                       unlabelled 8 bit input to MIME, the
+                                       character set to use by default.
+confSERVICE_SWITCH_FILE        ServiceSwitchFile
+                                       [/etc/service.switch] The file to use
+                                       for the service switch on systems that
+                                       do not have a system-defined switch.
+confHOSTS_FILE         HostsFile       [/etc/hosts] The file to use when doing
+                                       "file" type access of hosts names.
+confDIAL_DELAY         DialDelay       [0s] If a connection fails, wait this
+                                       long and try again.  Zero means "don't
+                                       retry".  This is to allow "dial on
+                                       demand" connections to have enough time
+                                       to complete a connection.
+confNO_RCPT_ACTION     NoRecipientAction
+                                       [none] What to do if there are no legal
+                                       recipient fields (To:, Cc: or Bcc:)
+                                       in the message.  Legal values can
+                                       be "none" to just leave the
+                                       nonconforming message as is, "add-to"
+                                       to add a To: header with all the
+                                       known recipients (which may expose
+                                       blind recipients), "add-apparently-to"
+                                       to do the same but use Apparently-To:
+                                       instead of To:, "add-bcc" to add an
+                                       empty Bcc: header, or
+                                       "add-to-undisclosed" to add the header
+                                       ``To: undisclosed-recipients:;''.
+confSAFE_FILE_ENV      SafeFileEnvironment
+                                       [undefined] If set, sendmail will do a
+                                       chroot() into this directory before
+                                       writing files.
+confCOLON_OK_IN_ADDR   ColonOkInAddr   [True unless Configuration Level > 6]
+                                       If set, colons are treated as a regular
+                                       character in addresses.  If not set,
+                                       they are treated as the introducer to
+                                       the RFC 822 "group" syntax.  Colons are
+                                       handled properly in route-addrs.  This
+                                       option defaults on for V5 and lower
+                                       configuration files.
+confMAX_QUEUE_RUN_SIZE MaxQueueRunSize [0] If set, limit the maximum size of
+                                       any given queue run to this number of
+                                       entries.  Essentially, this will stop
+                                       reading the queue directory after this
+                                       number of entries are reached; it does
+                                       _not_ pick the highest priority jobs,
+                                       so this should be as large as your
+                                       system can tolerate.  If not set, there
+                                       is no limit.
+confDONT_EXPAND_CNAMES DontExpandCnames
+                                       [False] If set, $[ ... $] lookups that
+                                       do DNS based lookups do not expand
+                                       CNAME records.  This currently violates
+                                       the published standards, but the IETF
+                                       seems to be moving toward legalizing
+                                       this.  For example, if "FTP.Foo.ORG"
+                                       is a CNAME for "Cruft.Foo.ORG", then
+                                       with this option set a lookup of
+                                       "FTP" will return "FTP.Foo.ORG"; if
+                                       clear it returns "Cruft.FOO.ORG".  N.B.
+                                       you may not see any effect until your
+                                       downstream neighbors stop doing CNAME
+                                       lookups as well.
+confFROM_LINE          UnixFromLine    [From $g  $d] The From_ line used
+                                       when sending to files or programs.
+confOPERATORS          OperatorChars   [.:%@!^/[]+] Address operator
+                                       characters.
+confSMTP_LOGIN_MSG     SmtpGreetingMessage
+                                       [$j Sendmail $v/$Z; $b]
+                                       The initial (spontaneous) SMTP
+                                       greeting message.  The word "ESMTP"
+                                       will be inserted between the first and
+                                       second words to convince other
+                                       sendmails to try to speak ESMTP.
+confDONT_INIT_GROUPS   DontInitGroups  [False] If set, the initgroups(3)
+                                       routine will never be invoked.  You
+                                       might want to do this if you are
+                                       running NIS and you have a large group
+                                       map, since this call does a sequential
+                                       scan of the map; in a large site this
+                                       can cause your ypserv to run
+                                       essentially full time.  If you set
+                                       this, agents run on behalf of users
+                                       will only have their primary
+                                       (/etc/passwd) group permissions.
+
+See also the description of OSTYPE for some parameters that can be
+tweaked (generally pathnames to mailers).
 
 
 +-----------+
@@ -1092,12 +1613,8 @@ ostype           Definitions describing a particular operating
 
 domain         Definitions describing a particular domain, referenced
                using the DOMAIN macro in the .mc file.  These are
-               site dependent; for example, we contribute "cs.exposed.m4"
-               and "cs.hidden.m4" which both describe hosts in the
-               CS.Berkeley.EDU subdomain; the former displays the local
-               hostname (e.g., mammoth.CS.Berkeley.EDU), whereas the
-               latter does its best to hide the identity of the local
-               workstation inside the CS subdomain.
+               site dependent; for example, "CS.Berkeley.EDU.m4"
+               describes hosts in the CS.Berkeley.EDU subdomain.
 
 mailer         Descriptions of mailers.   These are referenced using
                the MAILER macro in the .mc file.
@@ -1147,6 +1664,7 @@ RULESETS (* means built in to sendmail)
   96   Bottom half of Ruleset 3 (ruleset 6 in old sendmail)
   97   Hook for recursive ruleset 0 call (ruleset 7 in old sendmail)
   98   Local part of ruleset 0 (ruleset 8 in old sendmail)
+  99   Guaranteed null (for debugging)
 
 
 MAILERS
@@ -1156,22 +1674,23 @@ MAILERS
    2   uucp-*          UNIX-to-UNIX Copy Program
    3   netnews         Network News delivery
    4   fax             Sam Leffler's FlexFAX software
+   5   mail11          DECnet mailer
 
 
 MACROS
 
    A
    B   Bitnet Relay
-   C
+   C   DECnet Relay
    D   The local domain -- usually not needed
-   E
+   E   reserved for X.400 Relay
    F   FAX Relay
    G
    H   mail Hub (for mail clusters)
    I
    J
    K
-   L
+   L   Luser Relay
    M   Masquerade (who I claim to be)
    N
    O
@@ -1202,10 +1721,10 @@ CLASSES
    J
    K
    L   addresses that should not be forwarded to $R
-   M
+   M   domains that should be mapped to $M
    N
    O   operators that indicate network operations (cannot be in local names)
-   P   top level pseudo-domains: BITNET, FAX, UUCP, etc.
+   P   top level pseudo-domains: BITNET, DECNET, FAX, UUCP, etc.
    Q
    R
    S
index f4be0a3..d2aea4e 100644 (file)
@@ -1,4 +1,14 @@
-#      @(#)Makefile    8.5 (Berkeley) 12/1/93
+#      @(#)Makefile    8.17 (Berkeley) 9/12/95
+
+#
+#  This Makefile uses the new Berkeley "make" program.  See Makefile.dist
+#  for a more vanilla version.
+#
+#  Configuration files are created using "m4 file.mc > file.cf";
+#  this may be easier than tweaking the Makefile.  You do need to
+#  have a fairly modern M4 available (GNU m4 works).  On SunOS, use
+#  /usr/5bin/m4.
+#
 
 M4=    m4
 CHMOD= chmod
@@ -12,9 +22,7 @@ RM=   rm -f
        (cd ${.CURDIR} && $(M4) ${@:R}.mc > ${.OBJDIR}/$@)
        $(CHMOD) $(ROMODE) $@
 
-ALL=   ucbarpa.cf ucbvax.cf vangogh.cf \
-       chez.cf python.cf \
-       boat-anchor.cf chimera.cf pain.cf sun-lamp.cf trinity.cf \
+ALL=   boat-anchor.cf chimera.cf pain.cf sun-lamp.cf trinity.cf \
        clientproto.cf netbsd-proto.cf tcpproto.cf uucpproto.cf
 
 all: $(ALL)
@@ -30,15 +38,16 @@ distribution:
 
 # this is overkill, but....
 M4FILES=\
-       ../domain/Berkeley.m4 \
-       ../domain/cs.exposed.m4 \
-       ../domain/cs.hidden.m4 \
-       ../domain/eecs.hidden.m4 \
-       ../domain/s2k.m4 \
+       ../domain/Berkeley.EDU.m4 \
+       ../domain/CS.Berkeley.EDU.m4 \
+       ../domain/EECS.Berkeley.EDU.m4 \
+       ../domain/S2K.Berkeley.EDU.m4 \
        ../feature/allmasquerade.m4 \
        ../feature/always_add_domain.m4 \
+       ../feature/bestmx_is_local.m4 \
        ../feature/bitdomain.m4 \
        ../feature/domaintable.m4 \
+       ../feature/local_procmail.m4 \
        ../feature/mailertable.m4 \
        ../feature/nocanonify.m4 \
        ../feature/nodns.m4 \
@@ -46,15 +55,22 @@ M4FILES=\
        ../feature/nouucp.m4 \
        ../feature/nullclient.m4 \
        ../feature/redirect.m4 \
+       ../feature/smrsh.m4 \
+       ../feature/stickyhost.m4 \
        ../feature/use_cw_file.m4 \
        ../feature/uucpdomain.m4 \
        ../hack/cssubdomain.m4 \
        ../m4/cf.m4 \
+       ../m4/cfhead.m4 \
        ../m4/nullrelay.m4 \
        ../m4/proto.m4 \
        ../m4/version.m4 \
+       ../mailer/cyrus.m4 \
        ../mailer/fax.m4 \
        ../mailer/local.m4 \
+       ../mailer/mail11.m4 \
+       ../mailer/pop.m4 \
+       ../mailer/procmail.m4 \
        ../mailer/smtp.m4 \
        ../mailer/usenet.m4 \
        ../mailer/uucp.m4 \
index 77f122b..abdbf38 100644 (file)
@@ -35,7 +35,7 @@ divert(-1)
 #
 
 include(`../m4/cf.m4')
-VERSIONID(`@(#)boat-anchor.mc  $Revision: 1.1.1.1 $')
+VERSIONID(`@(#)boat-anchor.mc  $Revision: 1.2 $')
 OSTYPE(bsd4.4)dnl
 MAILER(local)dnl
 MAILER(smtp)dnl
diff --git a/usr.sbin/sendmail/cf/cf/chez.mc b/usr.sbin/sendmail/cf/cf/chez.mc
deleted file mode 100644 (file)
index 13f9519..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-divert(-1)
-#
-# Copyright (c) 1983 Eric P. Allman
-# Copyright (c) 1988, 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.
-#
-
-include(`../m4/cf.m4')
-VERSIONID(`@(#)chez.mc 8.1 (Berkeley) 6/7/93')
-OSTYPE(bsd4.4)dnl
-DOMAIN(cs.exposed)dnl
-define(`LOCAL_RELAY', vangogh.CS.Berkeley.EDU)dnl
-define(`MASQUERADE_NAME', vangogh.CS.Berkeley.EDU)dnl
-MAILER(local)dnl
-MAILER(smtp)dnl
-Fw/etc/sendmail.cw
index b86168c..cd171d7 100644 (file)
@@ -35,7 +35,7 @@ divert(-1)
 #
 #
 include(`../m4/cf.m4')
-VERSIONID(`@(#)pain.mc $Revision: 1.1.1.1 $')
+VERSIONID(`@(#)pain.mc $Revision: 1.2 $')
 OSTYPE(bsd4.4)dnl
 MAILER(local)dnl
 MAILER(smtp)dnl
index 902c1eb..a59ac03 100644 (file)
@@ -35,15 +35,20 @@ divert(-1)
 
 #
 #  This the prototype for a "null client" -- that is, a client that
-#  does nothing except forward all mail to a mail hub.
+#  does nothing except forward all mail to a mail hub.  IT IS NOT
+#  USABLE AS IS!!!
 #
 #  To use this, you MUST use the nullclient feature with the name of
-#  the mail hub as its argument.  You MAY also define an OSTYPE to
+#  the mail hub as its argument.  You MUST also define an `OSTYPE' to
 #  define the location of the queue directories and the like.
+#  In addition, you MAY select the nocanonify feature.  This causes
+#  addresses to be sent unqualified via the SMTP connection; normally
+#  they are qualifed with the masquerade name, which defaults to the
+#  name of the hub machine.
 #  Other than these, it should never contain any other lines.
 #
 
-include(`../m4/cf.m4')
-VERSIONID(`@(#)clientproto.mc  8.2 (Berkeley) 8/21/93')
+VERSIONID(`@(#)clientproto.mc  8.6 (Berkeley) 8/16/95')
 
+OSTYPE(unknown)
 FEATURE(nullclient, mailhost.$m)
index 0a3d849..fe094cf 100644 (file)
@@ -43,7 +43,7 @@ divert(-1)
 #
 
 include(`../m4/cf.m4')
-VERSIONID(`@(#)netbsd-proto.mc $Revision: 1.1.1.1 $')
+VERSIONID(`@(#)netbsd-proto.mc $Revision: 1.2 $')
 OSTYPE(bsd4.4)dnl
 MAILER(local)dnl
 MAILER(smtp)dnl
index f89e7c4..e6bb6e4 100644 (file)
@@ -35,7 +35,7 @@ divert(-1)
 #
 
 include(`../m4/cf.m4')
-VERSIONID(`@(#)pain.mc $Revision: 1.1.1.1 $')
+VERSIONID(`@(#)pain.mc $Revision: 1.2 $')
 OSTYPE(bsd4.4)dnl
 MAILER(local)dnl
 MAILER(smtp)dnl
diff --git a/usr.sbin/sendmail/cf/cf/python.mc b/usr.sbin/sendmail/cf/cf/python.mc
deleted file mode 100644 (file)
index ac23e61..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-divert(-1)
-#
-# Copyright (c) 1983 Eric P. Allman
-# Copyright (c) 1988, 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.
-#
-
-include(`../m4/cf.m4')
-VERSIONID(`@(#)python.mc       8.1 (Berkeley) 6/7/93')
-OSTYPE(bsd4.4)dnl
-DOMAIN(cs.exposed)dnl
-define(`LOCAL_RELAY', vangogh.CS.Berkeley.EDU)dnl
-define(`MASQUERADE_NAME', vangogh.CS.Berkeley.EDU)dnl
-MAILER(local)dnl
-MAILER(smtp)dnl
-
-# accept mail sent to the domain head
-DDBostic.COM
-
-LOCAL_RULE_0
-# accept mail sent to the domain head
-R< @ $D . > : $*               $@ $>7 $1               @here:... -> ...
-R$* $=O $* < @ $D . >          $@ $>7 $1 $2 $3         ...@here -> ...
-R$* < @ $D . >                 $#local $: $1           user@here -> user
index 37286b0..50e52b9 100644 (file)
@@ -35,7 +35,7 @@ divert(-1)
 #
 
 include(`../m4/cf.m4')
-VERSIONID(`@(#)sun-lamp.mc     $Revision: 1.1.1.1 $')
+VERSIONID(`@(#)sun-lamp.mc     $Revision: 1.2 $')
 OSTYPE(bsd4.4)dnl
 MASQUERADE_AS(NetBSD.ORG)dnl
 MAILER(local)dnl
index aa31ca1..7f43558 100644 (file)
@@ -37,14 +37,17 @@ divert(-1)
 #  This is the prototype file for a configuration that supports nothing
 #  but basic SMTP connections via TCP.
 #
-#  You may want to add an OSTYPE macro to get the location of various
-#  support files for your operating system environment.
+#  You MUST change the `OSTYPE' macro to specify the operating system
+#  on which this will run; this will set the location of various
+#  support files for your operating system environment.  You MAY
+#  create a domain file in ../domain and reference it by adding a
+#  `DOMAIN' macro after the `OSTYPE' macro.  I recommend that you
+#  first copy this to another file name so that new sendmail releases
+#  will not trash your changes.
 #
 
-include(`../m4/cf.m4')
-VERSIONID(`@(#)tcpproto.mc     8.2 (Berkeley) 8/21/93')
-
+VERSIONID(`@(#)tcpproto.mc     8.4 (Berkeley) 8/6/95')
+OSTYPE(unknown)
 FEATURE(nouucp)
-
 MAILER(local)
 MAILER(smtp)
index f27a86e..5a74234 100644 (file)
@@ -34,7 +34,7 @@ divert(-1)
 #
 
 include(`../m4/cf.m4')
-VERSIONID(`$Id: trinity.mc,v 1.1.1.1 1995/10/18 08:48:10 deraadt Exp $')
+VERSIONID(`$Id: trinity.mc,v 1.2 1996/01/29 01:41:06 dm Exp $')
 OSTYPE(bsd4.4)dnl
 define(`UUCP_RELAY', life.ai.mit.edu)dnl
 define(`BITNET_RELAY', mitvma.mit.edu)dnl
diff --git a/usr.sbin/sendmail/cf/cf/ucbarpa.mc b/usr.sbin/sendmail/cf/cf/ucbarpa.mc
deleted file mode 100644 (file)
index 21f35fd..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-divert(-1)
-#
-# Copyright (c) 1983 Eric P. Allman
-# Copyright (c) 1988, 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.
-#
-
-include(`../m4/cf.m4')
-VERSIONID(`@(#)ucbarpa.mc      8.1 (Berkeley) 6/7/93')
-DOMAIN(cs.exposed)dnl
-OSTYPE(bsd4.4)dnl
-MAILER(local)dnl
-MAILER(smtp)dnl
-MAILER(uucp)dnl
-SITECONFIG(uucp.ucbarpa, ucbarpa, U)
diff --git a/usr.sbin/sendmail/cf/cf/ucbvax.mc b/usr.sbin/sendmail/cf/cf/ucbvax.mc
deleted file mode 100644 (file)
index 249368f..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-divert(-1)
-#
-# Copyright (c) 1983 Eric P. Allman
-# Copyright (c) 1988, 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.
-#
-
-include(`../m4/cf.m4')
-VERSIONID(`@(#)ucbvax.mc       8.1 (Berkeley) 6/7/93')
-OSTYPE(bsd4.4)
-DOMAIN(cs.hidden)
-FEATURE(notsticky)
-MAILER(local)
-MAILER(smtp)
-MAILER(uucp)
-undefine(`UUCP_RELAY')dnl
-DDBerkeley.EDU
-
-# names for which we act as a local forwarding agent
-CF CS
-FF/etc/sendmail.cw
-
-# local UUCP connections, and our local uucp name
-SITECONFIG(uucp.ucbvax, ucbvax, U)
-
-# remote UUCP connections, and the machine they are on
-SITECONFIG(uucp.ucbarpa, ucbarpa.Berkeley.EDU, W)
-
-SITECONFIG(uucp.cogsci, cogsci.Berkeley.EDU, X)
-
-LOCAL_RULE_3
-# map old UUCP names into Internet names
-UUCPSMTP(bellcore,     bellcore.com)
-UUCPSMTP(decvax,       decvax.dec.com)
-UUCPSMTP(decwrl,       decwrl.dec.com)
-UUCPSMTP(hplabs,       hplabs.hp.com)
-UUCPSMTP(lbl-csam,     lbl-csam.arpa)
-UUCPSMTP(pur-ee,       ecn.purdue.edu)
-UUCPSMTP(purdue,       purdue.edu)
-UUCPSMTP(research,     research.att.com)
-UUCPSMTP(sdcarl,       sdcarl.ucsd.edu)
-UUCPSMTP(sdcsvax,      sdcsvax.ucsd.edu)
-UUCPSMTP(ssyx,         ssyx.ucsc.edu)
-UUCPSMTP(sun,          sun.com)
-UUCPSMTP(ucdavis,      ucdavis.ucdavis.edu)
-UUCPSMTP(ucivax,       ics.uci.edu)
-UUCPSMTP(ucla-cs,      cs.ucla.edu)
-UUCPSMTP(ucla-se,      seas.ucla.edu)
-UUCPSMTP(ucsbcsl,      ucsbcsl.ucsb.edu)
-UUCPSMTP(ucscc,                c.ucsc.edu)
-UUCPSMTP(ucsd,         ucsd.edu)
-UUCPSMTP(ucsfcgl,      cgl.ucsf.edu)
-UUCPSMTP(unmvax,       unmvax.cs.unm.edu)
-UUCPSMTP(uwvax,                spool.cs.wisc.edu)
-
-LOCAL_RULE_0
-
-# make sure we handle the local domain as absolute
-R$* <  @ $* $D > $*            $: $1 < @ $2 $D . > $3
-
-# handle names we forward for as though they were local, so we will use UDB
-R< @ $=F . $D . > : $*         $@ $>7 $2               @here:... -> ...
-R< @ $D . > : $*               $@ $>7 $1               @here:... -> ...
-R$* $=O $* < @ $=F . $D . >    $@ $>7 $1 $2 $3         ...@here -> ...
-R$* $=O $* < @ $D . >          $@ $>7 $1 $2 $3         ...@here -> ...
-
-R$* < @ $=F . $D . >           $#local $: $1           use UDB
-
-# handle local UUCP connections in the Berkeley.EDU domain
-R$+<@cnmat.$D . >              $#uucp$@cnmat$:$1
-R$+<@cnmat.CS.$D . >           $#uucp$@cnmat$:$1
-R$+<@craig.$D . >              $#uucp$@craig$:$1
-R$+<@craig.CS.$D . >           $#uucp$@craig$:$1
index c460d76..49c4a0d 100644 (file)
@@ -34,16 +34,20 @@ divert(-1)
 #
 
 #
-#  This is the prototype for a configuration that only supports UUCP.
+#  This is the prototype for a configuration that only supports UUCP
+#  and does not have DNS support at all.
 #
-#  You may want to add an OSTYPE macro to get the location of various
-#  support files for your operating system environment.
+#  You MUST change the `OSTYPE' macro to specify the operating system
+#  on which this will run; this will set the location of various
+#  support files for your operating system environment.  You MAY
+#  create a domain file in ../domain and reference it by adding a
+#  `DOMAIN' macro after the `OSTYPE' macro.  I recommend that you
+#  first copy this to another file name so that new sendmail releases
+#  will not trash your changes.
 #
 
-include(`../m4/cf.m4')
-VERSIONID(`@(#)uucpproto.mc    8.3 (Berkeley) 8/21/93')
-
+VERSIONID(`@(#)uucpproto.mc    8.5 (Berkeley) 8/6/95')
+OSTYPE(unknown)
 FEATURE(nodns)dnl
-
 MAILER(local)dnl
 MAILER(uucp)dnl
diff --git a/usr.sbin/sendmail/cf/cf/vangogh.mc b/usr.sbin/sendmail/cf/cf/vangogh.mc
deleted file mode 100644 (file)
index 2406364..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-divert(-1)
-#
-# Copyright (c) 1983 Eric P. Allman
-# Copyright (c) 1988, 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.
-#
-
-include(`../m4/cf.m4')
-VERSIONID(`@(#)vangogh.mc      8.2 (Berkeley) 1/26/94')
-DOMAIN(cs.exposed)dnl
-OSTYPE(bsd4.4)dnl
-MAILER(local)dnl
-MAILER(smtp)dnl
-define(`MCI_CACHE_SIZE', 5)
-Cw okeeffe.CS.Berkeley.EDU
-Cw python.CS.Berkeley.EDU
diff --git a/usr.sbin/sendmail/cf/domain/Berkeley.EDU.m4 b/usr.sbin/sendmail/cf/domain/Berkeley.EDU.m4
new file mode 100644 (file)
index 0000000..2bad42b
--- /dev/null
@@ -0,0 +1,45 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+divert(0)
+VERSIONID(`@(#)Berkeley.EDU.m4 8.9 (Berkeley) 10/5/95')
+DOMAIN(berkeley-only)dnl
+define(`BITNET_RELAY', `bitnet-relay.Berkeley.EDU')dnl
+define(`UUCP_RELAY', `uucp-relay.Berkeley.EDU')dnl
+define(`confFORWARD_PATH', `$z/.forward.$w:$z/.forward')dnl
+define(`confCW_FILE', `-o /etc/sendmail.cw')dnl
+define(`confDONT_INIT_GROUPS', True)dnl
+FEATURE(redirect)dnl
+FEATURE(use_cw_file)dnl
+FEATURE(stickyhost)dnl
diff --git a/usr.sbin/sendmail/cf/domain/CS.Berkeley.EDU.m4 b/usr.sbin/sendmail/cf/domain/CS.Berkeley.EDU.m4
new file mode 100644 (file)
index 0000000..97ccfb2
--- /dev/null
@@ -0,0 +1,40 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+divert(0)
+VERSIONID(`@(#)CS.Berkeley.EDU.m4      8.2 (Berkeley) 4/21/95')
+DOMAIN(Berkeley.EDU)dnl
+HACK(cssubdomain)dnl
+define(`confUSERDB_SPEC',
+       `/usr/sww/share/lib/users.cs.db,/usr/sww/share/lib/users.eecs.db')dnl
diff --git a/usr.sbin/sendmail/cf/domain/EECS.Berkeley.EDU.m4 b/usr.sbin/sendmail/cf/domain/EECS.Berkeley.EDU.m4
new file mode 100644 (file)
index 0000000..a41fc7e
--- /dev/null
@@ -0,0 +1,38 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+divert(0)
+VERSIONID(`@(#)EECS.Berkeley.EDU.m4    8.2 (Berkeley) 4/21/95')
+DOMAIN(Berkeley.EDU)dnl
+MASQUERADE_AS(EECS.Berkeley.EDU)dnl
diff --git a/usr.sbin/sendmail/cf/domain/S2K.Berkeley.EDU.m4 b/usr.sbin/sendmail/cf/domain/S2K.Berkeley.EDU.m4
new file mode 100644 (file)
index 0000000..4aed130
--- /dev/null
@@ -0,0 +1,38 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+divert(0)
+VERSIONID(`@(#)S2K.Berkeley.EDU.m4     8.2 (Berkeley) 4/21/95')
+DOMAIN(CS.Berkeley.EDU)dnl
+MASQUERADE_AS(postgres.Berkeley.EDU)dnl
diff --git a/usr.sbin/sendmail/cf/domain/berkeley-only.m4 b/usr.sbin/sendmail/cf/domain/berkeley-only.m4
new file mode 100644 (file)
index 0000000..ef71071
--- /dev/null
@@ -0,0 +1,40 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+divert(0)
+VERSIONID(`@(#)unspecified-domain.m4   8.2 (Berkeley) 4/21/95')
+errprint(`*** ERROR: You are trying to use the Berkeley sample configuration')
+errprint(`     files outside of the Computer Science Division at Berkeley.')
+errprint(`     The configuration (.mc) files must be customized to reference')
+errprint(`     domain files appropriate for your environment.')
diff --git a/usr.sbin/sendmail/cf/domain/generic.m4 b/usr.sbin/sendmail/cf/domain/generic.m4
new file mode 100644 (file)
index 0000000..6799ff8
--- /dev/null
@@ -0,0 +1,48 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+
+#
+#  The following is a generic domain file.  You should be able to
+#  use it anywhere.  If you want to customize it, copy it to a file
+#  named with your domain and make the edits; then, copy the appropriate
+#  .mc files and change `DOMAIN(generic)' to reference your updated domain
+#  files.
+#
+divert(0)
+VERSIONID(`@(#)generic.m4      8.2 (Berkeley) 4/21/95')
+define(`confFORWARD_PATH', `$z/.forward.$w:$z/.forward')dnl
+define(`confCW_FILE', `-o /etc/sendmail.cw')dnl
+FEATURE(redirect)dnl
+FEATURE(use_cw_file)dnl
diff --git a/usr.sbin/sendmail/cf/feature/bestmx_is_local.m4 b/usr.sbin/sendmail/cf/feature/bestmx_is_local.m4
new file mode 100644 (file)
index 0000000..23dff8c
--- /dev/null
@@ -0,0 +1,58 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+
+divert(0)
+VERSIONID(`@(#)bestmx_is_local.m4      8.2 (Berkeley) 10/29/95')
+divert(-1)
+
+LOCAL_CONFIG
+Kbestmx bestmx
+
+LOCAL_NET_CONFIG
+
+# If we are the best MX for a site, then we want to accept
+# its mail as local.  We assume we've already weeded out mail to
+# UUCP sites which are connected to us, which should also have
+# listed us as their best MX.
+#
+# Warning: this may generate a lot of extra DNS traffic -- a
+# lower cost method is to list all the expected best MX hosts
+# in $=w.  This should be fine (and easier to administer) for
+# low to medium traffic hosts.
+
+R$* < @ $* > $*                        $: $1 < @ $2 @@ $(bestmx $2 $) > $3
+R$* $=O $* < @ $* @@ $=w . > $*        $@ $>97 $1 $2 $3
+R$* < @ $* @@ $=w . > $*       $#local $: $1
+R$* < @ $* @@ $* > $*          $: $1 < @ $2 > $4
diff --git a/usr.sbin/sendmail/cf/feature/local_procmail.m4 b/usr.sbin/sendmail/cf/feature/local_procmail.m4
new file mode 100644 (file)
index 0000000..9087a69
--- /dev/null
@@ -0,0 +1,44 @@
+divert(-1)
+#
+# Copyright (c) 1994 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+
+divert(0)
+VERSIONID(`@(#)local_procmail.m4       8.3 (Berkeley) 10/29/95')
+divert(-1)
+
+define(`PROCMAIL_PATH',
+       ifelse(_ARG_, `', `/usr/local/bin/procmail', `_ARG_'))
+define(`LOCAL_MAILER_FLAGS', `SPfhn')
+define(`LOCAL_MAILER_PATH', PROCMAIL_PATH)
+define(`LOCAL_MAILER_ARGS', `procmail -Y -a $h -d $u')
diff --git a/usr.sbin/sendmail/cf/feature/masquerade_envelope.m4 b/usr.sbin/sendmail/cf/feature/masquerade_envelope.m4
new file mode 100644 (file)
index 0000000..1e60108
--- /dev/null
@@ -0,0 +1,40 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+
+divert(0)
+VERSIONID(`@(#)masquerade_envelope.m4  8.1 (Berkeley) 7/9/95')
+divert(-1)
+
+define(`_MASQUERADE_ENVELOPE_', 1)
index 5118923..027b8e3 100644 (file)
@@ -34,7 +34,9 @@ divert(-1)
 #
 
 divert(0)
-VERSIONID(`@(#)notsticky.m4    8.1 (Berkeley) 6/7/93')
+VERSIONID(`@(#)notsticky.m4    8.3 (Berkeley) 5/29/95')
+#
+#  This is now the default.  Use ``FEATURE(stickyhost)'' if you want
+#  the old default behaviour.
+#
 divert(-1)
-
-define(`_LOCAL_NOT_STICKY_', 1)
index 930f265..7543070 100644 (file)
@@ -32,9 +32,7 @@ PUSHDIVERT(-1)
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 #
-ifdef(`SMTP_MAILER_FLAGS',,
-       `define(`SMTP_MAILER_FLAGS',
-               `ifdef(`_OLD_SENDMAIL_', `L', `')')')
+ifdef(`SMTP_MAILER_FLAGS',, `define(`SMTP_MAILER_FLAGS', `')')
 define(_NULL_CLIENT_ONLY_, `1')
 ifelse(_ARG_, `', `errprint(`Feature "nullclient" requires argument')',
        `define(`MAIL_HUB', _ARG_)')
@@ -47,8 +45,17 @@ POPDIVERT
 #  sendmail.
 #
 
-VERSIONID(`@(#)nullclient.m4   8.2 (Berkeley) 8/21/93')
+VERSIONID(`@(#)nullclient.m4   8.6 (Berkeley) 6/29/95')
 
+PUSHDIVERT(6)
+# hub host (to which all mail is sent)
+DH`'ifdef(`MAIL_HUB', MAIL_HUB,
+       `errprint(`MAIL_HUB not defined for nullclient feature')')
+ifdef(`MASQUERADE_NAME',, `define(`MASQUERADE_NAME', MAIL_HUB)')dnl
+
+# route-addr separators
+C: : ,
+POPDIVERT
 PUSHDIVERT(7)
 ############################################
 ###   Null Client Mailer specification   ###
@@ -56,6 +63,9 @@ PUSHDIVERT(7)
 
 ifdef(`confRELAY_MAILER',,
        `define(`confRELAY_MAILER', `nullclient')')dnl
+ifdef(`confFROM_HEADER',,
+       `define(`confFROM_HEADER', <$g>)')dnl
 
-Mnullclient,   P=[IPC], F=CONCAT(mDFMuXa, SMTP_MAILER_FLAGS), A=IPC $h
+Mnullclient,   P=[IPC], F=CONCAT(mDFMuXa, SMTP_MAILER_FLAGS),ifdef(`SMTP_MAILER_MAX', ` M=SMTP_MAILER_MAX,')
+               A=IPC $h
 POPDIVERT
index 0f2199c..081db11 100644 (file)
@@ -34,13 +34,15 @@ divert(-1)
 #
 
 divert(0)
-VERSIONID(`@(#)redirect.m4     8.2 (Berkeley) 12/27/93')
+VERSIONID(`@(#)redirect.m4     8.4 (Berkeley) 6/25/95')
 divert(-1)
 
 
 PUSHDIVERT(3)
 # addresses sent to foo@host.REDIRECT will give a 551 error code
-R$* < @ $+ .REDIRECT. >        $# error $@ NOUSER $: "551 User not local; please try " <$1@$2>
+R$* < @ $+ .REDIRECT. >                $: $1 < @ $2 . REDIRECT . > < ${opMode} >
+R$* < @ $+ .REDIRECT. > <i>    $: $1 < @ $2 . REDIRECT. >
+R$* < @ $+ .REDIRECT. > < $- > $# error $@ 5.1.1 $: "551 User not local; please try " <$1@$2>
 POPDIVERT
 
 PUSHDIVERT(6)
diff --git a/usr.sbin/sendmail/cf/feature/smrsh.m4 b/usr.sbin/sendmail/cf/feature/smrsh.m4
new file mode 100644 (file)
index 0000000..6b4faab
--- /dev/null
@@ -0,0 +1,42 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+
+divert(0)
+VERSIONID(`@(#)smrsh.m4        8.2 (Berkeley) 11/11/95')
+divert(-1)
+
+ifdef(`_MAILER_local_',
+       `errprint(`*** FEATURE(smrsh) must occur before MAILER(local)')')dnl
+define(`LOCAL_SHELL_PATH', ifelse(_ARG_, `', `/usr/local/etc/smrsh', _ARG_))
diff --git a/usr.sbin/sendmail/cf/feature/stickyhost.m4 b/usr.sbin/sendmail/cf/feature/stickyhost.m4
new file mode 100644 (file)
index 0000000..bdd9c9a
--- /dev/null
@@ -0,0 +1,40 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+
+divert(0)
+VERSIONID(`@(#)stickyhost.m4   8.1 (Berkeley) 11/12/94')
+divert(-1)
+
+define(`_STICKY_LOCAL_DOMAIN_', 1)
diff --git a/usr.sbin/sendmail/cf/feature/use_ct_file.m4 b/usr.sbin/sendmail/cf/feature/use_ct_file.m4
new file mode 100644 (file)
index 0000000..c33bbfd
--- /dev/null
@@ -0,0 +1,46 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+
+divert(0)
+VERSIONID(`@(#)use_ct_file.m4  8.1 (Berkeley) 9/17/95')
+divert(-1)
+
+# if defined, the sendmail.cf will read the /etc/sendmail.ct file
+# to find the names of trusted users.  There should only be a few
+# of these, and normally this is done directly in the .cf file.
+
+define(`_USE_CT_FILE_', `')
+
+divert(0)
index 528cbff..4e54f41 100644 (file)
@@ -1,6 +1,6 @@
-divert(0)dnl
+divert(-1)
 #
-# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1983, 1995 Eric P. Allman
 # Copyright (c) 1988, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -33,117 +33,18 @@ divert(0)dnl
 # SUCH DAMAGE.
 #
 
+#
+#  This file is included so that multiple includes of cf.m4 will work
+#
 
-######################################################################
-######################################################################
-#####
-#####          SENDMAIL CONFIGURATION FILE
-#####
-define(`TEMPFILE', maketemp(/tmp/cfXXXXXX))dnl
-syscmd(sh ../sh/makeinfo.sh > TEMPFILE)dnl
-include(TEMPFILE)dnl
-syscmd(rm -f TEMPFILE)dnl
-#####
-######################################################################
-######################################################################
-
-divert(-1)
-
-changecom(\ 1)
-undefine(`format')
-undefine(`hpux')
-ifdef(`pushdef', `',
-       `errprint(`You need a newer version of M4, at least as new as
-System V or GNU')
-       include(NoSuchFile)')
-define(`PUSHDIVERT', `pushdef(`__D__', divnum)divert($1)')
-define(`POPDIVERT', `divert(__D__)popdef(`__D__')')
-define(`OSTYPE', `PUSHDIVERT(-1)define(`_ARG_', $2)include(../ostype/$1.m4)POPDIVERT`'')
-define(`MAILER',
-`ifdef(`_MAILER_$1_', `dnl`'',
-`define(`_MAILER_$1_', `')PUSHDIVERT(7)include(../mailer/$1.m4)POPDIVERT`'')')
-define(`DOMAIN', `PUSHDIVERT(-1)define(`_ARG_', $2)include(../domain/$1.m4)POPDIVERT`'')
-define(`FEATURE', `PUSHDIVERT(-1)define(`_ARG_', $2)include(../feature/$1.m4)POPDIVERT`'')
-define(`HACK', `PUSHDIVERT(-1)define(`_ARG_', $2)include(../hack/$1.m4)POPDIVERT`'')
-define(`OLDSENDMAIL', `define(`_OLD_SENDMAIL_', `')')
-define(`VERSIONID', ``#####  $1  #####'')
-define(`LOCAL_RULE_0', `divert(3)')
-define(`LOCAL_RULE_1',
-`divert(9)dnl
-#######################################
-###  Ruleset 1 -- Sender Rewriting  ###
-#######################################
-
-S1
-')
-define(`LOCAL_RULE_2',
-`divert(9)dnl
-##########################################
-###  Ruleset 2 -- Recipient Rewriting  ###
-##########################################
-
-S2
-')
-define(`LOCAL_RULE_3', `divert(2)')
-define(`LOCAL_CONFIG', `divert(6)')
-define(`LOCAL_NET_CONFIG', `define(`_LOCAL_RULES_', 1)divert(1)')
-define(`UUCPSMTP', `R DOL(*) < @ $1 .UUCP > DOL(*)     DOL(1) < @ $2 > DOL(2)')
-define(`CONCAT', `$1$2$3$4$5$6$7')
-define(`DOL', ``$'$1')
-define(`SITECONFIG',
-`CONCAT(D, $3, $2)
-define(`_CLASS_$3_', `')dnl
-ifelse($3, U, Cw$2 $2.UUCP, `dnl')
-define(`SITE', `ifelse(CONCAT($'2`, $3), SU,
-               CONCAT(CY, $'1`),
-               CONCAT(C, $3, $'1`))')
-sinclude(../siteconfig/$1.m4)')
-define(`EXPOSED_USER', `PUSHDIVERT(5)CE$1
-POPDIVERT`'dnl')
-define(`LOCAL_USER', `PUSHDIVERT(5)CL$1
-POPDIVERT`'dnl')
-define(`MASQUERADE_AS', `define(`MASQUERADE_NAME', $1)')
-
-m4wrap(`include(`../m4/proto.m4')')
-
-# set up default values for options
-define(`confMAILER_NAME', ``MAILER-DAEMON'')
-define(`confFROM_LINE', `From $g  $d')
-define(`confOPERATORS', `.:%@!^/[]')
-define(`confSMTP_LOGIN_MSG', `$j Sendmail $v/$Z ready at $b')
-define(`confSEVEN_BIT_INPUT', `False')
-define(`confALIAS_WAIT', `10')
-define(`confMIN_FREE_BLOCKS', `4')
-define(`confBLANK_SUB', `.')
-define(`confCON_EXPENSIVE', `False')
-define(`confCHECKPOINT_INTERVAL', `10')
-define(`confDELIVERY_MODE', `background')
-define(`confAUTO_REBUILD', `False')
-define(`confSAVE_FROM_LINES', `False')
-define(`confTEMP_FILE_MODE', `0600')
-define(`confMATCH_GECOS', `False')
-define(`confDEF_GROUP_ID', `1')
-define(`confMAX_HOP', `17')
-define(`confIGNORE_DOTS', `False')
-define(`confBIND_OPTS', `')
-define(`confMCI_CACHE_SIZE', `2')
-define(`confMCI_CACHE_TIMEOUT', `5m')
-define(`confUSE_ERRORS_TO', `False')
-define(`confLOG_LEVEL', `9')
-define(`confME_TOO', `False')
-define(`confCHECK_ALIASES', `True')
-define(`confOLD_STYLE_HEADERS', `True')
-define(`confPRIVACY_FLAGS', `authwarnings')
-define(`confSAFE_QUEUE', `True')
-define(`confMESSAGE_TIMEOUT', `5d/4h')
-define(`confTIME_ZONE', `USE_SYSTEM')
-define(`confDEF_USER_ID', `1')
-define(`confQUEUE_LA', `8')
-define(`confREFUSE_LA', `12')
-define(`confSEPARATE_PROC', `False')
-define(`confCW_FILE', `/etc/sendmail.cw')
-define(`confMIME_FORMAT_ERRORS', `True')
-define(`confTRY_NULL_MX_LIST', `False')
+# figure out where the CF files live
+ifdef(`_CF_DIR_', `',
+       `ifelse(__file__, `__file__',
+               `define(`_CF_DIR_', `../')',
+               `define(`_CF_DIR_',
+                       substr(__file__, 0, eval(len(__file__) - 8)))')')
 
 divert(0)dnl
-VERSIONID(`@(#)cf.m4   8.4 (Berkeley) 12/24/93')
+ifdef(`OSTYPE', `dnl',
+`include(_CF_DIR_`'m4/cfhead.m4)dnl
+VERSIONID(`@(#)cf.m4   8.24 (Berkeley) 8/16/95')')
diff --git a/usr.sbin/sendmail/cf/m4/cfhead.m4 b/usr.sbin/sendmail/cf/m4/cfhead.m4
new file mode 100644 (file)
index 0000000..46605e7
--- /dev/null
@@ -0,0 +1,147 @@
+#
+# Copyright (c) 1983, 1995 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+
+######################################################################
+######################################################################
+#####
+#####          SENDMAIL CONFIGURATION FILE
+#####
+define(`TEMPFILE', maketemp(/tmp/cfXXXXXX))dnl
+syscmd(sh _CF_DIR_`'sh/makeinfo.sh _CF_DIR_ > TEMPFILE)dnl
+include(TEMPFILE)dnl
+syscmd(rm -f TEMPFILE)dnl
+#####
+######################################################################
+######################################################################
+
+divert(-1)
+
+changecom(\ 1)
+undefine(`format')
+undefine(`hpux')
+ifdef(`pushdef', `',
+       `errprint(`You need a newer version of M4, at least as new as
+System V or GNU')
+       include(NoSuchFile)')
+define(`PUSHDIVERT', `pushdef(`__D__', divnum)divert($1)')
+define(`POPDIVERT', `divert(__D__)popdef(`__D__')')
+define(`OSTYPE',
+       `PUSHDIVERT(-1)
+       ifdef(`__OSTYPE__', `errprint(`duplicate OSTYPE'($1))')
+       define(`__OSTYPE__', $1)
+       define(`_ARG_', $2)
+       include(_CF_DIR_`'ostype/$1.m4)POPDIVERT`'')
+define(`MAILER',
+`ifdef(`_MAILER_$1_', `dnl`'',
+`define(`_MAILER_$1_', `')PUSHDIVERT(7)include(_CF_DIR_`'mailer/$1.m4)POPDIVERT`'')')
+define(`DOMAIN', `PUSHDIVERT(-1)define(`_ARG_', $2)include(_CF_DIR_`'domain/$1.m4)POPDIVERT`'')
+define(`FEATURE', `PUSHDIVERT(-1)define(`_ARG_', $2)include(_CF_DIR_`'feature/$1.m4)POPDIVERT`'')
+define(`HACK', `PUSHDIVERT(-1)define(`_ARG_', $2)include(_CF_DIR_`'hack/$1.m4)POPDIVERT`'')
+define(`VERSIONID', ``#####  $1  #####'')
+define(`LOCAL_RULE_0', `divert(3)')
+define(`LOCAL_RULE_1',
+`divert(9)dnl
+#######################################
+###  Ruleset 1 -- Sender Rewriting  ###
+#######################################
+
+S1
+')
+define(`LOCAL_RULE_2',
+`divert(9)dnl
+##########################################
+###  Ruleset 2 -- Recipient Rewriting  ###
+##########################################
+
+S2
+')
+define(`LOCAL_RULE_3', `divert(2)')
+define(`LOCAL_CONFIG', `divert(6)')
+define(`LOCAL_NET_CONFIG', `define(`_LOCAL_RULES_', 1)divert(1)')
+define(`UUCPSMTP', `R DOL(*) < @ $1 .UUCP > DOL(*)     DOL(1) < @ $2 > DOL(2)')
+define(`CONCAT', `$1$2$3$4$5$6$7')
+define(`DOL', ``$'$1')
+define(`SITECONFIG',
+`CONCAT(D, $3, $2)
+define(`_CLASS_$3_', `')dnl
+ifelse($3, U, Cw$2 $2.UUCP, `dnl')
+define(`SITE', `ifelse(CONCAT($'2`, $3), SU,
+               CONCAT(CY, $'1`),
+               CONCAT(C, $3, $'1`))')
+sinclude(_CF_DIR_`'siteconfig/$1.m4)')
+define(`EXPOSED_USER', `PUSHDIVERT(5)CE$1
+POPDIVERT`'dnl')
+define(`LOCAL_USER', `PUSHDIVERT(5)CL$1
+POPDIVERT`'dnl')
+define(`MASQUERADE_AS', `define(`MASQUERADE_NAME', $1)')
+define(`MASQUERADE_DOMAIN', `PUSHDIVERT(5)CM$1
+POPDIVERT`'dnl')
+define(`MASQUERADE_DOMAIN_FILE', `PUSHDIVERT(5)FM$1
+POPDIVERT`'dnl')
+define(`_OPTINS', `ifdef(`$1', `$2$1$3')')
+
+m4wrap(`include(_CF_DIR_`m4/proto.m4')')
+
+# set up default values for options
+define(`confMAILER_NAME', ``MAILER-DAEMON'')
+define(`confFROM_LINE', `From $g  $d')
+define(`confOPERATORS', `.:%@!^/[]+')
+define(`confSMTP_LOGIN_MSG', `$j Sendmail $v/$Z; $b')
+define(`confRECEIVED_HEADER', `$?sfrom $s $.$?_($?s$|from $.$_) $.by $j ($v/$Z)$?r with $r$. id $i$?u for $u$.; $b')
+define(`confSEVEN_BIT_INPUT', `False')
+define(`confEIGHT_BIT_HANDLING', `pass8')
+define(`confALIAS_WAIT', `10')
+define(`confMIN_FREE_BLOCKS', `100')
+define(`confBLANK_SUB', `.')
+define(`confCON_EXPENSIVE', `False')
+define(`confDELIVERY_MODE', `background')
+define(`confTEMP_FILE_MODE', `0600')
+define(`confMCI_CACHE_SIZE', `2')
+define(`confMCI_CACHE_TIMEOUT', `5m')
+define(`confUSE_ERRORS_TO', `False')
+define(`confLOG_LEVEL', `9')
+define(`confCHECK_ALIASES', `False')
+define(`confOLD_STYLE_HEADERS', `True')
+define(`confPRIVACY_FLAGS', `authwarnings')
+define(`confSAFE_QUEUE', `True')
+define(`confTO_QUEUERETURN', `5d')
+define(`confTO_QUEUEWARN', `4h')
+define(`confTIME_ZONE', `USE_SYSTEM')
+define(`confDEF_USER_ID', `1:1')
+define(`confCW_FILE', `/etc/sendmail.cw')
+define(`confMIME_FORMAT_ERRORS', `True')
+define(`confFORWARD_PATH', `$z/.forward.$w:$z/.forward')
+
+divert(0)dnl
+VERSIONID(`@(#)cfhead.m4       8.3 (Berkeley) 9/15/95')
index c79d179..799f3ab 100644 (file)
@@ -1,6 +1,6 @@
 divert(-1)
 #
-# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1983, 1995 Eric P. Allman
 # Copyright (c) 1988, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -34,7 +34,7 @@ divert(-1)
 #
 divert(0)
 
-VERSIONID(`@(#)nullrelay.m4    8.5 (Berkeley) 2/1/94')
+VERSIONID(`@(#)nullrelay.m4    8.10 (Berkeley) 9/29/95')
 
 #
 #  This configuration applies only to relay-only hosts.  They send
@@ -45,201 +45,6 @@ VERSIONID(`@(#)nullrelay.m4 8.5 (Berkeley) 2/1/94')
 #      This is based on a prototype done by Bryan Costales of ICSI.
 #
 
-# hub host (to which all mail is sent)
-DH`'ifdef(`MAIL_HUB', MAIL_HUB,
-       `errprint(`MAIL_HUB not defined for nullclient feature')')
-
-# name from which everyone will appear to come
-DM`'ifdef(`MASQUERADE_NAME', MASQUERADE_NAME, MAIL_HUB)
-
-# route-addr separators
-C: : ,
-
-undivert(6)dnl
-
-######################
-#   Special macros   #
-######################
-
-# SMTP initial login message
-De`'confSMTP_LOGIN_MSG
-
-# UNIX initial From header format
-Dl`'confFROM_LINE
-
-# my name for error messages
-Dn`'confMAILER_NAME
-
-# delimiter (operator) characters
-Do`'confOPERATORS
-
-# format of a total name
-Dq<$g>
-include(`../m4/version.m4')
-
-###############
-#   Options   #
-###############
-
-# strip message body to 7 bits on input?
-O7`'confSEVEN_BIT_INPUT
-
-# no aliases here
-
-# substitution for space (blank) characters
-OB`'confBLANK_SUB
-
-# default delivery mode
-Od`'confDELIVERY_MODE
-
-# error message header/file
-ifdef(`confERROR_MESSAGE',
-       OE`'confERROR_MESSAGE,
-       #OE/etc/sendmail.oE)
-
-# error mode
-ifdef(`confERROR_MODE',
-       Oe`'confERROR_MODE,
-       #Oep)
-
-# save Unix-style "From_" lines at top of header?
-Of`'confSAVE_FROM_LINES
-
-# temporary file mode
-OF`'confTEMP_FILE_MODE
-
-# default GID
-Og`'confDEF_GROUP_ID
-
-# maximum hop count
-Oh`'confMAX_HOP
-
-# location of help file
-OH`'ifdef(`HELP_FILE', HELP_FILE, /usr/lib/sendmail.hf)
-
-# ignore dots as terminators in incoming messages?
-Oi`'confIGNORE_DOTS
-
-# Insist that the BIND name server be running to resolve names
-ifdef(`confBIND_OPTS',
-       OI`'confBIND_OPTS,
-       #OI)
-
-# deliver MIME-encapsulated error messages?
-Oj`'confMIME_FORMAT_ERRORS
-
-# open connection cache size
-Ok`'confMCI_CACHE_SIZE
-
-# open connection cache timeout
-OK`'confMCI_CACHE_TIMEOUT
-
-# use Errors-To: header?
-Ol`'confUSE_ERRORS_TO
-
-# log level
-OL`'confLOG_LEVEL
-
-# send to me too, even in an alias expansion?
-Om`'confME_TOO
-
-# default messages to old style headers if no special punctuation?
-Oo`'confOLD_STYLE_HEADERS
-
-# SMTP daemon options
-ifdef(`confDAEMON_OPTIONS',
-       OO`'confDAEMON_OPTIONS,
-       #OOPort=esmtp)
-
-# privacy flags
-Op`'confPRIVACY_FLAGS
-
-# who (if anyone) should get extra copies of error messages
-ifdef(`confCOPY_ERRORS_TO',
-       OP`'confCOPY_ERRORS_TO,
-       #OPPostmaster)
-
-# slope of queue-only function
-ifdef(`confQUEUE_FACTOR',
-       Oq`'confQUEUE_FACTOR,
-       #Oq600000)
-
-# queue directory
-OQ`'ifdef(`QUEUE_DIR', QUEUE_DIR, /var/spool/mqueue)
-
-# read timeout -- now OK per RFC 1123 section 5.3.2
-ifdef(`confREAD_TIMEOUT',
-       Or`'confREAD_TIMEOUT,
-       #Ordatablock=10m)
-
-# queue up everything before forking?
-Os`'confSAFE_QUEUE
-
-# status file
-OS`'ifdef(`STATUS_FILE', STATUS_FILE, /etc/sendmail.st)
-
-# default message timeout interval
-OT`'confMESSAGE_TIMEOUT
-
-# time zone handling:
-#  if undefined, use system default
-#  if defined but null, use TZ envariable passed in
-#  if defined and non-null, use that info
-ifelse(confTIME_ZONE, `USE_SYSTEM', `#Ot',
-       confTIME_ZONE, `USE_TZ', `Ot',
-       `Ot`'confTIME_ZONE')
-
-# default UID
-Ou`'confDEF_USER_ID
-
-# deliver each queued job in a separate process?
-OY`'confSEPARATE_PROC
-
-# work class factor
-ifdef(`confWORK_CLASS_FACTOR',
-       Oz`'confWORK_CLASS_FACTOR,
-       #Oz1800)
-
-# work time factor
-ifdef(`confWORK_TIME_FACTOR',
-       OZ`'confWORK_TIME_FACTOR,
-       #OZ90000)
-
-###########################
-#   Message precedences   #
-###########################
-
-Pfirst-class=0
-Pspecial-delivery=100
-Plist=-30
-Pbulk=-60
-Pjunk=-100
-
-#####################
-#   Trusted users   #
-#####################
-
-Troot
-Tdaemon
-Tuucp
-
-#########################
-#   Format of headers   #
-#########################
-
-H?P?Return-Path: $g
-HReceived: $?sfrom $s $.$?_($_) $.by $j ($v/$Z)$?r with $r$. id $i$?u for $u$.; $b
-H?D?Resent-Date: $a
-H?D?Date: $a
-H?F?Resent-From: $q
-H?F?From: $q
-H?x?Full-Name: $x
-HSubject:
-# HPosted-Date: $a
-# H?l?Received-Date: $b
-H?M?Resent-Message-Id: <$t.$i@$j>
-H?M?Message-Id: <$t.$i@$j>
-#\f
 ######################################################################
 ######################################################################
 #####
@@ -253,9 +58,20 @@ H?M?Message-Id: <$t.$i@$j>
 ###########################################
 S3
 
-# handle null input and list syntax (translate to <@> special case)
+# handle null input
 R$@                    $@ <@>
-R$*:;$*                        $@ $1 :; <@>
+
+# strip group: syntax (not inside angle brackets!) and trailing semicolon
+R$*                    $: $1 <@>                       mark addresses
+R$* < $* > $* <@>      $: $1 < $2 > $3                 unmark <addr>
+R$* :: $* <@>          $: $1 :: $2                     unmark node::addr
+R:`include': $* <@>    $: :`include': $1                       unmark :`include':...
+R$* : $* <@>           $: $2                           strip colon if marked
+R$* <@>                        $: $1                           unmark
+R$* ;                  $: $1                           strip trailing semi
+
+# null input now results from list:; syntax
+R$@                    $@ :; <@>
 
 # basic textual canonicalization -- note RFC733 heuristic here
 R$*<$*>$*<$*>$*                $2$3<$4>$5                      strip multiple <> <>
@@ -291,6 +107,17 @@ R$*:;<@>           $#error $@ USAGE $: "list:; syntax illegal for recipient addresses"
 # pass everything else to a relay host
 R$*                    $#_RELAY_ $@ $H $: $1
 
+
+##################################################
+###  Ruleset 4 -- Final Output Post-rewriting  ###
+##################################################
+S4
+
+R$* <@>                        $@                              handle <> and list:;
+
+# strip trailing dot off before passing to nullclient relay
+R$* @ $+ .             $1 @ $2
+
 #\f
 ######################################################################
 ######################################################################
index 2845729..89b3215 100644 (file)
@@ -1,6 +1,6 @@
 divert(-1)
 #
-# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1983, 1995 Eric P. Allman
 # Copyright (c) 1988, 1993
 #      The Regents of the University of California.  All rights reserved.
 #
@@ -34,32 +34,54 @@ divert(-1)
 #
 divert(0)
 
-VERSIONID(`@(#)proto.m4        8.45 (Berkeley) 3/4/94')
+VERSIONID(`@(#)proto.m4        8.100 (Berkeley) 12/3/95')
 
 MAILER(local)dnl
 
-ifdef(`_OLD_SENDMAIL_',
-`define(`_SET_95_', 5)dnl
-define(`_SET_96_', 6)dnl
-define(`_SET_97_', 7)dnl
-define(`_SET_98_', 8)dnl
-define(`confDOMAIN_NAME',
-       `ifdef(`NEED_DOMAIN', `$w.$d', `$w')')dnl',
-`# level 5 config file format
-V5
-define(`_SET_95_', 95)dnl
-define(`_SET_96_', 96)dnl
-define(`_SET_97_', 97)dnl
-define(`_SET_98_', 98)dnl')
-ifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `smtp')')dnl
-ifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')dnl
+# level 6 config file format
+V6/Berkeley
+divert(-1)
+
+# do some sanity checking
+ifdef(`__OSTYPE__',,
+       `errprint(`*** ERROR: No system type defined (use OSTYPE macro)')')
+
+# pick our default mailers
+ifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `smtp')')
+ifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')
 ifdef(`confRELAY_MAILER',,
        `define(`confRELAY_MAILER',
                `ifdef(`_MAILER_smtp_', `relay',
-                       `ifdef(`_MAILER_uucp', `suucp', `unknown')')')')dnl
+                       `ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')')
+ifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')')
 define(`_SMTP_', `confSMTP_MAILER')dnl         for readability only
 define(`_LOCAL_', `confLOCAL_MAILER')dnl       for readability only
 define(`_RELAY_', `confRELAY_MAILER')dnl       for readability only
+define(`_UUCP_', `confUUCP_MAILER')dnl         for readability only
+
+# back compatibility with old config files
+ifdef(`confDEF_GROUP_ID',
+       `errprint(`*** confDEF_GROUP_ID is obsolete.')
+        errprint(`    Use confDEF_USER_ID with a colon in the value instead.')')
+ifdef(`confREAD_TIMEOUT',
+       `errprint(`*** confREAD_TIMEOUT is obsolete.')
+        errprint(`    Use individual confTO_<timeout> parameters instead.')')
+ifdef(`confMESSAGE_TIMEOUT',
+       `define(`_ARG_', index(confMESSAGE_TIMEOUT, /))
+        ifelse(_ARG_, -1,
+               `define(`confTO_QUEUERETURN', confMESSAGE_TIMEOUT)',
+               `define(`confTO_QUEUERETURN',
+                       substr(confMESSAGE_TIMEOUT, 0, _ARG_))
+                define(`confTO_QUEUEWARN',
+                       substr(confMESSAGE_TIMEOUT, eval(_ARG_+1)))')')
+ifdef(`confMIN_FREE_BLOCKS', `ifelse(index(confMIN_FREE_BLOCKS, /), -1,,
+       `errprint(`*** compound confMIN_FREE_BLOCKS is obsolete.')
+        errprint(`    Use confMAX_MESSAGE_SIZE for the second part of the value.')')')
+
+# clean option definitions below....
+define(`_OPTION', `ifdef(`$2', `O $1=$2', `#O $1`'ifelse($3, `',, `=$3')')')dnl
+
+divert(0)dnl
 
 ##################
 #   local info   #
@@ -70,14 +92,12 @@ ifdef(`USE_CW_FILE',
 `# file containing names of hosts for which we receive email
 Fw`'confCW_FILE',
        `dnl')
-ifdef(`confDOMAIN_NAME', `
+
 # my official domain name
-Dj`'confDOMAIN_NAME',
-       `dnl')
+# ... define this only if sendmail cannot automatically determine your domain
+ifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM')
 
-ifdef(`_NULL_CLIENT_ONLY_',
-`include(../m4/nullrelay.m4)m4exit',
-       `dnl')
+ifdef(`_NULL_CLIENT_ONLY_', `divert(-1)')dnl
 
 CP.
 
@@ -92,6 +112,13 @@ ifdef(`BITNET_RELAY',
 DB`'BITNET_RELAY
 CPBITNET
 
+')dnl
+ifdef(`DECNET_RELAY',
+`define(`_USE_DECNET_SYNTAX_', 1)dnl
+# DECnet relay host
+DC`'DECNET_RELAY
+CPDECNET
+
 ')dnl
 ifdef(`FAX_RELAY',
 `# FAX relay host
@@ -102,240 +129,276 @@ CPFAX
 # "Smart" relay host (may be null)
 DS`'ifdef(`SMART_HOST', SMART_HOST)
 
+# place to which unknown users should be forwarded
+ifdef(`LUSER_RELAY', `', `#')dnl
+Kuser user -m -a<>
+ifdef(`LUSER_RELAY',
+       `DL`'LUSER_RELAY',
+       `#DLname_of_luser_relay')
+
+# operators that cannot be in local usernames (i.e., network indicators)
+CO @ % ifdef(`_NO_UUCP_', `', `!')
+
+# a class with just dot (for identifying canonical names)
+C..
+
+# Mailer table (overriding domains)
 ifdef(`MAILER_TABLE',
-`# Mailer table (overriding domains)
-Kmailertable MAILER_TABLE
+       `Kmailertable MAILER_TABLE',
+       `#Kmailertable dbm /etc/mailertable')
 
-')dnl
+# Domain table (adding domains)
 ifdef(`DOMAIN_TABLE',
-`# Domain table (adding domains)
-Kdomaintable DOMAIN_TABLE
+       `Kdomaintable DOMAIN_TABLE',
+       `#Kdomaintable dbm /etc/domaintable')
 
-')dnl
 # who I send unqualified names to (null means deliver locally)
 DR`'ifdef(`LOCAL_RELAY', LOCAL_RELAY)
 
 # who gets all local email traffic ($R has precedence for unqualified names)
 DH`'ifdef(`MAIL_HUB', MAIL_HUB)
 
-# who I masquerade as (null for no masquerading)
-DM`'ifdef(`MASQUERADE_NAME', MASQUERADE_NAME)
-
 # class L: names that should be delivered locally, even if we have a relay
 # class E: names that should be exposed as from this host, even if we masquerade
-#CLroot
-CEroot
+# class M: domains that should be converted to $M
+#CL root
+CE root
 undivert(5)dnl
 
-# operators that cannot be in local usernames (i.e., network indicators)
-CO @ % ifdef(`_NO_UUCP_', `', `!')
-
-# a class with just dot (for identifying canonical names)
-C..
-
-ifdef(`_OLD_SENDMAIL_', `dnl',
-`# dequoting map
-Kdequote dequote')
-
-undivert(6)dnl
-
-######################
-#   Special macros   #
-######################
-
-# SMTP initial login message
-De`'confSMTP_LOGIN_MSG
+# dequoting map
+Kdequote dequote
 
-# UNIX initial From header format
-Dl`'confFROM_LINE
+divert(0)dnl   # end of nullclient diversion
+# who I masquerade as (null for no masquerading) (see also $=M)
+DM`'ifdef(`MASQUERADE_NAME', MASQUERADE_NAME)
 
 # my name for error messages
-Dn`'confMAILER_NAME
-
-# delimiter (operator) characters
-Do`'confOPERATORS
+ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
 
-# format of a total name
-Dq`'ifdef(`confFROM_HEADER', confFROM_HEADER,
-       ifdef(`_OLD_SENDMAIL_', `$g$?x ($x)$.', `$?x$x <$g>$|$g$.'))
-include(`../m4/version.m4')
+undivert(6)dnl
+include(_CF_DIR_`m4/version.m4')
 
 ###############
 #   Options   #
 ###############
 
 # strip message body to 7 bits on input?
-O7`'confSEVEN_BIT_INPUT
+_OPTION(SevenBitInput, `confSEVEN_BIT_INPUT')
 
-# wait (in minutes) for alias file rebuild
-Oa`'confALIAS_WAIT
+# 8-bit data handling
+_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', adaptive)
 
-# location of alias file
-OA`'ifdef(`ALIAS_FILE', `ALIAS_FILE', /etc/aliases)
+ifdef(`_NULL_CLIENT_ONLY_', `dnl', `
+# wait for alias file rebuild (default units: minutes)
+_OPTION(AliasWait, `confALIAS_WAIT', 5m)
 
+# location of alias file
+O AliasFile=ifdef(`ALIAS_FILE', `ALIAS_FILE', /etc/aliases)
+')
 # minimum number of free blocks on filesystem
-Ob`'confMIN_FREE_BLOCKS
+_OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', 100)
+
+# maximum message size
+_OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', 1000000)
 
 # substitution for space (blank) characters
-OB`'confBLANK_SUB
+_OPTION(BlankSub, `confBLANK_SUB', _)
 
 # avoid connecting to "expensive" mailers on initial submission?
-Oc`'confCON_EXPENSIVE
+_OPTION(HoldExpensive, `confCON_EXPENSIVE')
 
 # checkpoint queue runs after every N successful deliveries
-OC`'confCHECKPOINT_INTERVAL
+_OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', 10)
 
 # default delivery mode
-Od`'confDELIVERY_MODE
+_OPTION(DeliveryMode, `confDELIVERY_MODE', background)
 
 # automatically rebuild the alias database?
-OD`'confAUTO_REBUILD
+_OPTION(AutoRebuildAliases, `confAUTO_REBUILD')
 
 # error message header/file
-ifdef(`confERROR_MESSAGE',
-       OE`'confERROR_MESSAGE,
-       #OE/etc/sendmail.oE)
+_OPTION(ErrorHeader, `confERROR_MESSAGE', /etc/sendmail.oE)
 
 # error mode
-ifdef(`confERROR_MODE',
-       Oe`'confERROR_MODE,
-       #Oep)
+_OPTION(ErrorMode, `confERROR_MODE', print)
 
 # save Unix-style "From_" lines at top of header?
-Of`'confSAVE_FROM_LINES
+_OPTION(SaveFromLine, `confSAVE_FROM_LINES')
 
 # temporary file mode
-OF`'confTEMP_FILE_MODE
+_OPTION(TempFileMode, `confTEMP_FILE_MODE', 0600)
 
 # match recipients against GECOS field?
-OG`'confMATCH_GECOS
-
-# default GID
-Og`'confDEF_GROUP_ID
+_OPTION(MatchGECOS, `confMATCH_GECOS')
 
 # maximum hop count
-Oh`'confMAX_HOP
+_OPTION(MaxHopCount, `confMAX_HOP', 17)
 
 # location of help file
-OH`'ifdef(`HELP_FILE', HELP_FILE, /usr/lib/sendmail.hf)
+O HelpFile=ifdef(`HELP_FILE', HELP_FILE, /usr/lib/sendmail.hf)
 
 # ignore dots as terminators in incoming messages?
-Oi`'confIGNORE_DOTS
+_OPTION(IgnoreDots, `confIGNORE_DOTS')
 
-# Insist that the BIND name server be running to resolve names
-ifdef(`confBIND_OPTS',
-       OI`'confBIND_OPTS,
-       #OI)
+# name resolver options
+_OPTION(ResolverOptions, `confBIND_OPTS', +AAONLY)
 
 # deliver MIME-encapsulated error messages?
-Oj`'confMIME_FORMAT_ERRORS
+_OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS')
 
 # Forward file search path
-ifdef(`confFORWARD_PATH',
-       OJ`'confFORWARD_PATH,
-       #OJ/var/forward/$u:$z/.forward.$w:$z/.forward)
+_OPTION(ForwardPath, `confFORWARD_PATH', /var/forward/$u:$z/.forward.$w:$z/.forward)
 
 # open connection cache size
-Ok`'confMCI_CACHE_SIZE
+_OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', 2)
 
 # open connection cache timeout
-OK`'confMCI_CACHE_TIMEOUT
+_OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', 5m)
 
 # use Errors-To: header?
-Ol`'confUSE_ERRORS_TO
+_OPTION(UseErrorsTo, `confUSE_ERRORS_TO')
 
 # log level
-OL`'confLOG_LEVEL
+_OPTION(LogLevel, `confLOG_LEVEL', 10)
 
 # send to me too, even in an alias expansion?
-Om`'confME_TOO
+_OPTION(MeToo, `confME_TOO')
 
 # verify RHS in newaliases?
-On`'confCHECK_ALIASES
+_OPTION(CheckAliases, `confCHECK_ALIASES')
 
 # default messages to old style headers if no special punctuation?
-Oo`'confOLD_STYLE_HEADERS
+_OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS')
 
 # SMTP daemon options
-ifdef(`confDAEMON_OPTIONS',
-       OO`'confDAEMON_OPTIONS,
-       #OOPort=esmtp)
+_OPTION(DaemonPortOptions, `confDAEMON_OPTIONS', Port=esmtp)
 
 # privacy flags
-Op`'confPRIVACY_FLAGS
+_OPTION(PrivacyOptions, `confPRIVACY_FLAGS', authwarnings)
 
 # who (if anyone) should get extra copies of error messages
-ifdef(`confCOPY_ERRORS_TO',
-       OP`'confCOPY_ERRORS_TO,
-       #OPPostmaster)
+_OPTION(PostMasterCopy, `confCOPY_ERRORS_TO', Postmaster)
 
 # slope of queue-only function
-ifdef(`confQUEUE_FACTOR',
-       Oq`'confQUEUE_FACTOR,
-       #Oq600000)
+_OPTION(QueueFactor, `confQUEUE_FACTOR', 600000)
 
 # queue directory
-OQ`'ifdef(`QUEUE_DIR', QUEUE_DIR, /var/spool/mqueue)
-
-# read timeout -- now OK per RFC 1123 section 5.3.2
-ifdef(`confREAD_TIMEOUT',
-       Or`'confREAD_TIMEOUT,
-       #Ordatablock=10m)
+O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, /var/spool/mqueue)
+
+# timeouts (many of these)
+_OPTION(Timeout.initial, `confTO_INITIAL', 5m)
+_OPTION(Timeout.helo, `confTO_HELO', 5m)
+_OPTION(Timeout.mail, `confTO_MAIL', 10m)
+_OPTION(Timeout.rcpt, `confTO_RCPT', 1h)
+_OPTION(Timeout.datainit, `confTO_DATAINIT', 5m)
+_OPTION(Timeout.datablock, `confTO_DATABLOCK', 1h)
+_OPTION(Timeout.datafinal, `confTO_DATAFINAL', 1h)
+_OPTION(Timeout.rset, `confTO_RSET', 5m)
+_OPTION(Timeout.quit, `confTO_QUIT', 2m)
+_OPTION(Timeout.misc, `confTO_MISC', 2m)
+_OPTION(Timeout.command, `confTO_COMMAND', 1h)
+_OPTION(Timeout.ident, `confTO_IDENT', 30s)
+_OPTION(Timeout.fileopen, `confTO_FILEOPEN', 60s)
+_OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', 5d)
+_OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', 5d)
+_OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', 2d)
+_OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', 7d)
+_OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', 4h)
+_OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', 4h)
+_OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', 1h)
+_OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', 12h)
+
+# should we not prune routes in route-addr syntax addresses?
+_OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES')
 
 # queue up everything before forking?
-Os`'confSAFE_QUEUE
+_OPTION(SuperSafe, `confSAFE_QUEUE')
 
 # status file
-OS`'ifdef(`STATUS_FILE', STATUS_FILE, /etc/sendmail.st)
-
-# default message timeout interval
-OT`'confMESSAGE_TIMEOUT
+O StatusFile=ifdef(`STATUS_FILE', `STATUS_FILE', /etc/sendmail.st)
 
 # time zone handling:
 #  if undefined, use system default
 #  if defined but null, use TZ envariable passed in
 #  if defined and non-null, use that info
-ifelse(confTIME_ZONE, `USE_SYSTEM', `#Ot',
-       confTIME_ZONE, `USE_TZ', `Ot',
-       `Ot`'confTIME_ZONE')
+ifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
+       confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
+       `O TimeZoneSpec=confTIME_ZONE')
 
-# default UID
-Ou`'confDEF_USER_ID
+# default UID (can be username or userid:groupid)
+_OPTION(DefaultUser, `confDEF_USER_ID', nobody)
 
 # list of locations of user database file (null means no lookup)
-OU`'ifdef(`confUSERDB_SPEC', `confUSERDB_SPEC')
+_OPTION(UserDatabaseSpec, `confUSERDB_SPEC', /etc/userdb)
 
 # fallback MX host
-ifdef(`confFALLBACK_MX',
-       OV`'confFALLBACK_MX,
-       #OVfall.back.host.net)
+_OPTION(FallbackMXhost, `confFALLBACK_MX', fall.back.host.net)
 
 # if we are the best MX host for a site, try it directly instead of config err
-Ow`'confTRY_NULL_MX_LIST
+_OPTION(TryNullMXList, `confTRY_NULL_MX_LIST')
 
 # load average at which we just queue messages
-Ox`'confQUEUE_LA
+_OPTION(QueueLA, `confQUEUE_LA', 8)
 
 # load average at which we refuse connections
-OX`'confREFUSE_LA
+_OPTION(RefuseLA, `confREFUSE_LA', 12)
 
 # work recipient factor
-ifdef(`confWORK_RECIPIENT_FACTOR',
-       Oy`'confWORK_RECIPIENT_FACTOR,
-       #Oy30000)
+_OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', 30000)
 
 # deliver each queued job in a separate process?
-OY`'confSEPARATE_PROC
+_OPTION(ForkEachJob, `confSEPARATE_PROC')
 
 # work class factor
-ifdef(`confWORK_CLASS_FACTOR',
-       Oz`'confWORK_CLASS_FACTOR,
-       #Oz1800)
+_OPTION(ClassFactor, `confWORK_CLASS_FACTOR', 1800)
 
 # work time factor
-ifdef(`confWORK_TIME_FACTOR',
-       OZ`'confWORK_TIME_FACTOR,
-       #OZ90000)
+_OPTION(RetryFactor, `confWORK_TIME_FACTOR', 90000)
+
+# shall we sort the queue by hostname first?
+_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', priority)
+
+# minimum time in queue before retry
+_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', 30m)
+
+# default character set
+_OPTION(DefaultCharSet, `confDEF_CHAR_SET', iso-8859-1)
+
+# service switch file (ignored on Solaris, Ultrix, OSF/1, others)
+_OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', /etc/service.switch)
+
+# hosts file (normally /etc/hosts)
+_OPTION(HostsFile, `confHOSTS_FILE', /etc/hosts)
+
+# dialup line delay on connection failure
+_OPTION(DialDelay, `confDIAL_DELAY', 10s)
+
+# action to take if there are no recipients in the message
+_OPTION(NoRecipientAction, `confNO_RCPT_ACTION', add-to-undisclosed)
+
+# chrooted environment for writing to files
+_OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', /arch)
+
+# are colons OK in addresses?
+_OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR')
+
+# how many jobs can you process in the queue?
+_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', 10000)
+
+# shall I avoid expanding CNAMEs (violates protocols)?
+_OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES')
+
+# SMTP initial login message (old $e macro)
+_OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG')
+
+# UNIX initial From header format (old $l macro)
+_OPTION(UnixFromLine, `confFROM_LINE')
+
+# delimiter (operator) characters (old $o macro)
+_OPTION(OperatorChars, `confOPERATORS')
+
+# shall I avoid calling initgroups(3) because of high NIS costs?
+_OPTION(DontInitGroups, `confDONT_INIT_GROUPS')
 
 ###########################
 #   Message precedences   #
@@ -351,26 +414,33 @@ Pjunk=-100
 #   Trusted users   #
 #####################
 
+# this is equivalent to setting class "t"
+ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `/etc/sendmail.ct')
 Troot
 Tdaemon
 Tuucp
+ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
 
 #########################
 #   Format of headers   #
 #########################
 
+ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
 H?P?Return-Path: $g
-HReceived: $?sfrom $s $.$?_($?s$|from $.$_) $.by $j ($v/$Z)$?r with $r$. id $i$?u for $u$.; $b
+HReceived: confRECEIVED_HEADER
 H?D?Resent-Date: $a
 H?D?Date: $a
-H?F?Resent-From: $q
-H?F?From: $q
+H?F?Resent-From: confFROM_HEADER
+H?F?From: confFROM_HEADER
 H?x?Full-Name: $x
 HSubject:
 # HPosted-Date: $a
 # H?l?Received-Date: $b
 H?M?Resent-Message-Id: <$t.$i@$j>
 H?M?Message-Id: <$t.$i@$j>
+ifdef(`_NULL_CLIENT_ONLY_',
+       `include(_CF_DIR_`'m4/nullrelay.m4)m4exit',
+       `dnl')
 #\f
 ######################################################################
 ######################################################################
@@ -390,53 +460,69 @@ S3
 # handle null input (translate to <@> special case)
 R$@                    $@ <@>
 
-# basic textual canonicalization -- note RFC733 heuristic here
-R$*<$*>$*<$*>$*                $2$3<$4>$5                      strip multiple <> <>
-R$*<$*<$+>$*>$*                <$3>$5                          2-level <> nesting
-R$*<>$*                        $@ <@>                          MAIL FROM:<> case
-R$*<$+>$*              $2                              basic RFC821/822 parsing
-
-# handle list:; syntax as special case
-R$*:;$*                        $@ $1 :; <@>
+# strip group: syntax (not inside angle brackets!) and trailing semicolon
+R$*                    $: $1 <@>                       mark addresses
+R$* < $* > $* <@>      $: $1 < $2 > $3                 unmark <addr>
+R$* :: $* <@>          $: $1 :: $2                     unmark node::addr
+R:`include': $* <@>    $: :`include': $1                       unmark :`include':...
+R$* : $* <@>           $: $2                           strip colon if marked
+R$* <@>                        $: $1                           unmark
+R$* ;                  $: $1                           strip trailing semi
+
+# null input now results from list:; syntax
+R$@                    $@ :; <@>
+
+# strip angle brackets -- note RFC733 heuristic to get innermost item
+R$*                    $: < $1 >                       housekeeping <>
+R$+ < $* >                < $2 >                       strip excess on left
+R< $* > $+                < $1 >                       strip excess on right
+R<>                    $@ < @ >                        MAIL FROM:<> case
+R< $+ >                        $: $1                           remove housekeeping <>
 
 # make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
 R@ $+ , $+             @ $1 : $2                       change all "," to ":"
 
 # localize and dispose of route-based addresses
-R@ $+ : $+             $@ $>_SET_96_ < @$1 > : $2              handle <route-addr>
+R@ $+ : $+             $@ $>96 < @$1 > : $2            handle <route-addr>
 
 # find focus for list syntax
-R $+ : $* ; @ $+       $@ $>_SET_96_ $1 : $2 ; < @ $3 >        list syntax
+R $+ : $* ; @ $+       $@ $>96 $1 : $2 ; < @ $3 >      list syntax
 R $+ : $* ;            $@ $1 : $2;                     list syntax
 
 # find focus for @ syntax addresses
 R$+ @ $+               $: $1 < @ $2 >                  focus on domain
 R$+ < $+ @ $+ >                $1 $2 < @ $3 >                  move gaze right
-R$+ < @ $+ >           $@ $>_SET_96_ $1 < @ $2 >               already canonical
+R$+ < @ $+ >           $@ $>96 $1 < @ $2 >             already canonical
 
 # do some sanity checking
 R$* < @ $* : $* > $*   $1 < @ $2 $3 > $4               nix colons in addrs
 
 ifdef(`_NO_UUCP_', `dnl',
 `# convert old-style addresses to a domain-based address
-R$- ! $+               $@ $>_SET_96_ $2 < @ $1 .UUCP > resolve uucp names
-R$+ . $- ! $+          $@ $>_SET_96_ $3 < @ $1 . $2 >          domain uucps
-R$+ ! $+               $@ $>_SET_96_ $2 < @ $1 .UUCP > uucp subdomains')
-
+R$- ! $+               $@ $>96 $2 < @ $1 .UUCP >       resolve uucp names
+R$+ . $- ! $+          $@ $>96 $3 < @ $1 . $2 >                domain uucps
+R$+ ! $+               $@ $>96 $2 < @ $1 .UUCP >       uucp subdomains
+')
+ifdef(`_USE_DECNET_SYNTAX_',
+`# convert node::user addresses into a domain-based address
+R$- :: $+              $@ $>96 $2 < @ $1 .DECNET >     resolve DECnet names
+R$- . $- :: $+         $@ $>96 $3 < @ $1.$2 .DECNET >  numeric DECnet addr
+',
+       `dnl')
 # if we have % signs, take the rightmost one
 R$* % $*               $1 @ $2                         First make them all @s.
 R$* @ $* @ $*          $1 % $2 @ $3                    Undo all but the last.
-R$* @ $*               $@ $>_SET_96_ $1 < @ $2 >               Insert < > and finish
+R$* @ $*               $@ $>96 $1 < @ $2 >             Insert < > and finish
 
 # else we must be a local name
+R$*                    $@ $>96 $1
 
 
 ################################################
-###  Ruleset _SET_96_ -- bottom half of ruleset 3  ###
+###  Ruleset 96 -- bottom half of ruleset 3  ###
 ################################################
 
-#  At this point, everything should be in a "local_part<@domain>extra" format.
-S`'_SET_96_
+S96
 
 # handle special cases for local names
 R$* < @ localhost > $*         $: $1 < @ $j . > $2             no domain at all
@@ -446,10 +532,11 @@ ifdef(`_NO_UUCP_', `dnl',
 R$* < @ [ $+ ] > $*            $: $1 < @@ [ $2 ] > $3          mark [a.b.c.d]
 R$* < @@ $=w > $*              $: $1 < @ $j . > $3             self-literal
 R$* < @@ $+ > $*               $@ $1 < @ $2 > $3               canon IP addr
-ifdef(`DOMAIN_TABLE', `
-# look up unqualified domains in the domain table
-R$* < @ $- > $*                        $: $1 < @ $(domaintable $2 $) > $3',
-`dnl')
+
+# look up domains in the domain table
+ifdef(`DOMAIN_TABLE', `', `#')dnl
+R$* < @ $+ > $*                $: $1 < @ $(domaintable $2 $) > $3
+
 undivert(2)dnl
 
 ifdef(`_NO_UUCP_', `dnl',
@@ -470,9 +557,7 @@ ifdef(`_CLASS_Y_',
 
 # try UUCP traffic as a local address
 R$* < @ $+ . UUCP > $*         $: $1 < @ $[ $2 $] . UUCP . > $3
-ifdef(`_OLD_SENDMAIL_',
-`R$* < @ $+ . $+ . UUCP . > $*         $@ $1 < @ $2 . $3 . > $4',
-`R$* < @ $+ . . UUCP . > $*            $@ $1 < @ $2 . > $3')')
+R$* < @ $+ . . UUCP . > $*             $@ $1 < @ $2 . > $3')
 ')
 ifdef(`_NO_CANONIFY_', `dnl',
 `# pass to name server to make hostname canonical
@@ -492,11 +577,14 @@ R$* < @ $j > $*                   $: $1 < @ $j . > $2
 ##################################################
 S4
 
-R$*<@>                 $@ $1                           handle <> and list:;
+R$* <@>                        $@                              handle <> and list:;
 
 # strip trailing dot off possibly canonical name
 R$* < @ $+ . > $*      $1 < @ $2 > $3
 
+# eliminate internal code -- should never get this far!
+R$* < @ *LOCAL* > $*   $1 < @ $j > $2
+
 # externalize local domain info
 R$* < $+ > $*          $1 $2 $3                        defocus
 R@ $+ : @ $+ : $+      @ $1 , @ $2 : $3                <route-addr> canonical
@@ -506,17 +594,21 @@ ifdef(`_NO_UUCP_', `dnl',
 `# UUCP must always be presented in old form
 R$+ @ $- . UUCP                $2!$1                           u@h.UUCP => h!u')
 
+ifdef(`_USE_DECNET_SYNTAX_',
+`# put DECnet back in :: form
+R$+ @ $+ . DECNET      $2 :: $1                        u@h.DECNET => h::u',
+       `dnl')
 # delete duplicate local names
 R$+ % $=w @ $=w                $1 @ $j                         u%host@host => u@host
 
 
 
 ##############################################################
-###   Ruleset _SET_97_ -- recanonicalize and call ruleset zero   ###
+###   Ruleset 97 -- recanonicalize and call ruleset zero   ###
 ###               (used for recursive calls)              ###
 ##############################################################
 
-S`'_SET_97_
+S`'97
 R$*                    $: $>3 $1
 R$*                    $@ $>0 $1
 
@@ -527,62 +619,68 @@ R$*                       $@ $>0 $1
 
 S0
 
-R<@>                   $#_LOCAL_ $: <>                 special case error msgs
-R$* : $* ;             $#error $@ USAGE $: "list:; syntax illegal for recipient addresses"
-R<@ $+>                        $#error $@ USAGE $: "user address required"
-R<$* : $* >            $#error $@ USAGE $: "colon illegal in host name part"
+R<@>                   $#_LOCAL_ $: <@>                special case error msgs
+R$* : $* ; <@>         $#error $@ 5.1.3 $: "list:; syntax illegal for recipient addresses"
+R<@ $+>                        $#error $@ 5.1.1 $: "user address required"
+R$* <$* : $* > $*      $#error $@ 5.1.1 $: "colon illegal in host name part"
+R$* < @ . > $*         $#error $@ 5.1.2 $: "invalid host name"
 
 ifdef(`_MAILER_smtp_',
 `# handle numeric address spec
-R$* < @ [ $+ ] > $*    $: $>_SET_98_ $1 < @ [ $2 ] > $3        numeric internet spec
+R$* < @ [ $+ ] > $*    $: $>98 $1 < @ [ $2 ] > $3      numeric internet spec
 R$* < @ [ $+ ] > $*    $#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3    still numeric: send',
        `dnl')
 
 # now delete the local info -- note $=O to find characters that cause forwarding
-R$* < @ > $*           $@ $>_SET_97_ $1                user@ => user
-R< @ $=w . > : $*      $@ $>_SET_97_ $2                @here:... -> ...
-R$* $=O $* < @ $=w . > $@ $>_SET_97_ $1 $2 $3          ...@here -> ...
+R$* < @ > $*           $@ $>97 $1              user@ => user
+R< @ $=w . > : $*      $@ $>97 $2              @here:... -> ...
+R$- < @ $=w . >                $: $(dequote $1 $) < @ $2 . >   dequote "foo"@here
+R$* $=O $* < @ $=w . > $@ $>97 $1 $2 $3                ...@here -> ...
 
 # handle local hacks
-R$*                    $: $>_SET_98_ $1
+R$*                    $: $>98 $1
 
 # short circuit local delivery so forwarded email works
-ifdef(`_LOCAL_NOT_STICKY_',
-`R$=L < @ $=w . >              $#_LOCAL_ $: @ $1                       special local names
-R$+ < @ $=w . >                $#_LOCAL_ $: $1                 dispose directly',
-`R$+ < @ $=w . >               $: $1 < @ $2 . @ $H >           first try hub
-ifdef(`_OLD_SENDMAIL_',
-`R$+ < $+ @ $-:$+ >    $# $3 $@ $4 $: $1 < $2 >        yep ....
-R$+ < $+ @ $+ >                $#relay $@ $3 $: $1 < $2 >      yep ....
-R$+ < $+ @ >           $#_LOCAL_ $: $1                 nope, local address',
-`R$+ < $+ @ $+ >               $#_LOCAL_ $: $1                 yep ....
-R$+ < $+ @ >           $#_LOCAL_ $: @ $1                       nope, local address')')
-ifdef(`MAILER_TABLE',
-`
+ifdef(`_MAILER_usenet_',
+`R$+ . USENET < @ $=w . >      $#usenet $: $1          handle usenet specially',
+       `dnl')
+ifdef(`_STICKY_LOCAL_DOMAIN_',
+`R$+ < @ $=w . >               $: < $H > $1 < @ $2 . >         first try hub
+R< $+ > $+ < $+ >      $>95 < $1 > $2 < $3 >           yep ....
+R< > $+ + $* < $+ >    $#_LOCAL_ $: $1 + $2            plussed name?
+R< > $+ < $+ >         $#_LOCAL_ $: @ $1                       nope, local address',
+`R$=L < @ $=w . >      $#_LOCAL_ $: @ $1               special local names
+R$+ < @ $=w . >                $#_LOCAL_ $: $1                 regular local name')
+
+define(`X', ifdef(`MAILER_TABLE', `', `#'))dnl
 # not local -- try mailer table lookup
-R$* <@ $+ > $*         $: < $2 > $1 < @ $2 > $3        extract host name
-R< $+ . > $*           $: < $1 > $2                    strip trailing dot
-R< $+ > $*             $: < $(mailertable $1 $) > $2   lookup
-R< $- : $+ > $*                $# $1 $@ $2 $: $3               check -- resolved?
-R< $+ > $*             $: $>90 <$1> $2                 try domain',
-`dnl')
+X`'R$* <@ $+ > $*              $: < $2 > $1 < @ $2 > $3        extract host name
+X`'R< $+ . > $*                $: < $1 > $2                    strip trailing dot
+X`'R< $+ > $*          $: < $(mailertable $1 $) > $2   lookup
+X`'R< error : $- $+ > $*       $#error $@ $1 $: $2             check -- error?
+X`'R< $- : $+ > $*     $# $1 $@ $2 $: $3               check -- resolved?
+X`'R< $+ > $*          $: $>90 <$1> $2                 try domain
+undefine(`X')dnl
 undivert(4)dnl
 
 ifdef(`_NO_UUCP_', `dnl',
 `# resolve remotely connected UUCP links (if any)
 ifdef(`_CLASS_V_',
-`R$* < @ $=V . UUCP . > $*             $: $>_SET_95_ < $V > $1 <@$2.UUCP.> $3',
+`R$* < @ $=V . UUCP . > $*             $: $>95 < $V > $1 <@$2.UUCP.> $3',
        `dnl')
 ifdef(`_CLASS_W_',
-`R$* < @ $=W . UUCP . > $*             $: $>_SET_95_ < $W > $1 <@$2.UUCP.> $3',
+`R$* < @ $=W . UUCP . > $*             $: $>95 < $W > $1 <@$2.UUCP.> $3',
        `dnl')
 ifdef(`_CLASS_X_',
-`R$* < @ $=X . UUCP . > $*             $: $>_SET_95_ < $X > $1 <@$2.UUCP.> $3',
+`R$* < @ $=X . UUCP . > $*             $: $>95 < $X > $1 <@$2.UUCP.> $3',
        `dnl')')
 
 # resolve fake top level domains by forwarding to other hosts
 ifdef(`BITNET_RELAY',
-`R$*<@$+.BITNET.>$*    $: $>_SET_95_ < $B > $1 <@$2.BITNET.> $3        user@host.BITNET',
+`R$*<@$+.BITNET.>$*    $: $>95 < $B > $1 <@$2.BITNET.> $3      user@host.BITNET',
+       `dnl')
+ifdef(`DECNET_RELAY',
+`R$*<@$+.DECNET.>$*    $: $>95 < $C > $1 <@$2.DECNET.> $3      user@host.DECNET',
        `dnl')
 ifdef(`_MAILER_pop_',
 `R$+ < @ POP. >                $#pop $: $1                     user@POP',
@@ -590,15 +688,15 @@ ifdef(`_MAILER_pop_',
 ifdef(`_MAILER_fax_',
 `R$+ < @ $+ .FAX. >    $#fax $@ $2 $: $1               user@host.FAX',
 `ifdef(`FAX_RELAY',
-`R$*<@$+.FAX.>$*               $: $>_SET_95_ < $F > $1 <@$2.FAX.> $3   user@host.FAX',
+`R$*<@$+.FAX.>$*               $: $>95 < $F > $1 <@$2.FAX.> $3 user@host.FAX',
        `dnl')')
 
 ifdef(`UUCP_RELAY',
 `# forward non-local UUCP traffic to our UUCP relay
-R$*<@$*.UUCP.>$*               $: $>_SET_95_ < $Y > $1 <@$2.UUCP.> $3  uucp mail',
+R$*<@$*.UUCP.>$*               $: $>95 < $Y > $1 <@$2.UUCP.> $3        uucp mail',
 `ifdef(`_MAILER_uucp_',
 `# forward other UUCP traffic straight to UUCP
-R$* < @ $+ .UUCP. > $*         $#uucp $@ $2 $: $1 < @ $2 .UUCP. > $3   user@host.UUCP',
+R$* < @ $+ .UUCP. > $*         $#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3 user@host.UUCP',
        `dnl')')
 ifdef(`_MAILER_usenet_', `
 # addresses sent to net.group.USENET will get forwarded to a newsgroup
@@ -610,23 +708,16 @@ ifdef(`_LOCAL_RULES_',
 undivert(1)', `dnl')
 
 # pass names that still have a host to a smarthost (if defined)
-R$* < @ $* > $*                $: $>_SET_95_ < $S > $1 < @ $2 > $3     glue on smarthost name
+R$* < @ $* > $*                $: $>95 < $S > $1 < @ $2 > $3   glue on smarthost name
 
 # deal with other remote names
 ifdef(`_MAILER_smtp_',
 `R$* < @$* > $*                $#_SMTP_ $@ $2 $: $1 < @ $2 > $3                user@host.domain',
-`R$* < @$* > $*                $#error $@NOHOST $: Unrecognized host name $2')
-
-ifdef(`_OLD_SENDMAIL_',
-`# forward remaining names to local relay, if any
-R$=L                   $#_LOCAL_ $: $1                 special local names
-R$+                    $: $>_SET_95_ < $R > $1                 try relay
-R$+                    $: $>_SET_95_ < $H > $1                 try hub
-R$+                    $#_LOCAL_ $: $1                 no relay or hub: local',
+`R$* < @$* > $*                $#error $@ 5.1.2 $: Unrecognized host name $2')
 
-`# if this is quoted, strip the quotes and try again
+# if this is quoted, strip the quotes and try again
 R$+                    $: $(dequote $1 $)              strip quotes
-R$+ $=O $+             $@ $>_SET_97_ $1 $2 $3                  try again
+R$+ $=O $+             $@ $>97 $1 $2 $3                        try again
 
 # handle locally delivered names
 R$=L                   $#_LOCAL_ $: @ $1                       special local names
@@ -634,49 +725,90 @@ R$+                       $#_LOCAL_ $: $1                 regular local names
 
 ###########################################################################
 ###   Ruleset 5 -- special rewriting after aliases have been expanded   ###
-###               (new sendmail only)                                  ###
 ###########################################################################
 
 S5
 
+# deal with plussed users so aliases work nicely
+R$+ + *                        $#_LOCAL_ $@ $&h $: $1
+R$+ + $*               $#_LOCAL_ $@ $2 $: $1 + *
+
+# prepend an empty "forward host" on the front
+R$+                    $: <> $1
+
+define(`X', ifdef(`LUSER_RELAY', `', `#'))dnl
+# send unrecognized local users to a relay host
+X`'R< > $+ + $*                $: < $L . > $( user $1 $) + $2
+X`'R< > $+             $: < $L . > $( user $1 $)       look up user
+X`'R< $* > $+ <> $*    $: < > $2 $3                    found; strip $L
+X`'R< $* . > $+                $: < $1 > $2                    strip extra dot
+undefine(`X')dnl
+
+# handle plussed local names
+R< > $+ + $*           $#_LOCAL_ $@ $2 $: $1
+
 # see if we have a relay or a hub
-R$+                    $: < $R > $1                    try relay
 R< > $+                        $: < $H > $1                    try hub
+R< > $+                        $: < $R > $1                    try relay
 R< > $+                        $@ $1                           nope, give up
-R< $- : $+ > $+                $: $>_SET_95_ < $1 : $2 > $3 < @ $2 >
-R< $+ > $+             $@ $>_SET_95_ < $1 > $2 < @ $1 >')
-ifdef(`MAILER_TABLE',
-`
+R< $- : $+ > $+                $: $>95 < $1 : $2 > $3 < @ $2 >
+R< $+ > $+             $@ $>95 < $1 > $2 < @ $1 >
 
 ###################################################################
 ###  Ruleset 90 -- try domain part of mailertable entry        ###
-###               (new sendmail only)                          ###
 ###################################################################
 
-S90
-R$* <$- . $+ > $*      $: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
-R$* <$- : $+ > $*      $# $2 $@ $3 $: $4               check -- resolved?
-R$* < . $+ > $*                $@ $>90 $1 . <$2> $3            no -- strip & try again
-R$* < $* > $*          $: < $(mailertable . $@ $1$2 $) > $3    try "."
-R<$- : $+ > $*         $# $1 $@ $2 $: $3               "." found?
-R< $* > $*             $@ $2                           no mailertable match',
-`dnl')
+define(`X', ifdef(`MAILER_TABLE', `', `#'))dnl
+X`'S90
+X`'R$* <$- . $+ > $*   $: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
+X`'R$* <$- : $+ > $*   $# $2 $@ $3 $: $4               check -- resolved?
+X`'R$* < . $+ > $*     $@ $>90 $1 . <$2> $3            no -- strip & try again
+X`'R$* < $* > $*               $: < $(mailertable . $@ $1$2 $) > $3    try "."
+X`'R<$- : $+ > $*              $# $1 $@ $2 $: $3               "." found?
+X`'R< $* > $*          $@ $2                           no mailertable match
+undefine(`X')dnl
 
 ###################################################################
-###  Ruleset _SET_95_ -- canonify mailer:host syntax to triple ###
+###  Ruleset 95 -- canonify mailer:host syntax to triple       ###
 ###################################################################
 
-S`'_SET_95_
+S95
 R< > $*                        $@ $1                           strip off null relay
 R< $- : $+ > $*                $# $1 $@ $2 $: $3               try qualified mailer
 R< $=w > $*            $@ $2                           delete local host
 R< $+ > $*             $#_RELAY_ $@ $1 $: $2           use unqualified mailer
 
 ###################################################################
-###  Ruleset _SET_98_ -- local part of ruleset zero (can be null)      ###
+###  Ruleset 93 -- convert header names to masqueraded form    ###
+###################################################################
+
+S93
+R$=E < @ *LOCAL* >     $@ $1 < @ $j . >                leave exposed
+R$=E < @ $=M . >       $@ $1 < @ $2 . >
+ifdef(`_LIMITED_MASQUERADE_', `#')dnl
+R$=E < @ $=w . >       $@ $1 < @ $2 . >
+R$* < @ $=M . > $*     $: $1 < @ $2 . @ $M > $3        convert masqueraded doms
+ifdef(`_LIMITED_MASQUERADE_', `#')dnl
+R$* < @ $=w . > $*     $: $1 < @ $2 . @ $M > $3
+R$* < @ *LOCAL* > $*   $: $1 < @ $j . @ $M > $2
+R$* < @ $+ @ > $*      $@ $1 < @ $2 > $3               $M is null
+R$* < @ $+ @ $+ > $*   $@ $1 < @ $3 . > $4             $M is not null
+
+###################################################################
+###  Ruleset 94 -- convert envelope names to masqueraded form  ###
+###################################################################
+
+S94
+ifdef(`_MASQUERADE_ENVELOPE_', `', `#')dnl
+R$+                    $@ $>93 $1
+ifdef(`_MASQUERADE_ENVELOPE_', `#', `')dnl
+R$* < @ *LOCAL* > $*   $: $1 < @ $j . > $2
+
+###################################################################
+###  Ruleset 98 -- local part of ruleset zero (can be null)    ###
 ###################################################################
 
-S`'_SET_98_
+S98
 undivert(3)dnl
 #\f
 ######################################################################
index d7abdd6..fb8e96e 100644 (file)
@@ -32,8 +32,8 @@ divert(-1)
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 #
-VERSIONID(`@(#)version.m4      8.6.12.1 (Berkeley) 3/28/95')
+VERSIONID(`@(#)version.m4      8.7.3.1 (Berkeley) 12/3/95')
 #
 divert(0)
 # Configuration version number
-DZ8.6.12
+DZ8.7.3`'ifdef(`confCF_VERSION', `/confCF_VERSION')
diff --git a/usr.sbin/sendmail/cf/mailer/cyrus.m4 b/usr.sbin/sendmail/cf/mailer/cyrus.m4
new file mode 100644 (file)
index 0000000..f531137
--- /dev/null
@@ -0,0 +1,44 @@
+PUSHDIVERT(-1)
+#
+#      (C) Copyright 1995 by Carnegie Mellon University
+# 
+#                      All Rights Reserved
+# 
+# Permission to use, copy, modify, and distribute this software and its 
+# documentation for any purpose and without fee is hereby granted, 
+# 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 CMU not be
+# used in advertising or publicity pertaining to distribution of the
+# software without specific, written prior permission.  
+# 
+# CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+# CMU 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.
+#
+#      Contributed to Berkeley by John Gardiner Myers <jgm+@CMU.EDU>.
+# 
+
+ifdef(`CYRUS_MAILER_FLAGS',, `define(`CYRUS_MAILER_FLAGS', `A5@')')
+ifdef(`CYRUS_MAILER_PATH',, `define(`CYRUS_MAILER_PATH', /usr/cyrus/bin/deliver)')
+ifdef(`CYRUS_MAILER_ARGS',, `define(`CYRUS_MAILER_ARGS', `deliver -e -m $h -- $u')')
+ifdef(`CYRUS_BB_MAILER_FLAGS',, `define(`CYRUS_BB_MAILER_FLAGS', `')')
+ifdef(`CYRUS_BB_MAILER_ARGS',, `define(`CYRUS_BB_MAILER_ARGS', `deliver -e -m $u')')
+
+POPDIVERT
+
+##################################################
+###   Cyrus Mailer specification               ###
+##################################################
+
+VERSIONID(`@(#)cyrus.m4        8.2 (Carnegie Mellon) 9/12/95')
+
+Mcyrus,                P=CYRUS_MAILER_PATH, F=CONCAT(`lsDFMnP', CYRUS_MAILER_FLAGS), S=10, R=20/40, T=X-Unix,
+               ifdef(`CYRUS_MAILER_MAX', `M=CYRUS_MAILER_MAX, ')A=CYRUS_MAILER_ARGS
+
+Mcyrusbb,      P=CYRUS_MAILER_PATH, F=CONCAT(`lsDFMnP', CYRUS_MAILER_FLAGS), S=10, R=20/40, T=X-Unix,
+               ifdef(`CYRUS_MAILER_MAX', `M=CYRUS_MAILER_MAX, ')A=CYRUS_BB_MAILER_ARGS
index 0c98a3b..777fe01 100644 (file)
@@ -44,7 +44,10 @@ POPDIVERT
 ###   FAX Mailer specification   ###
 ####################################
 
-VERSIONID(`@(#)fax.m4  8.2 (Berkeley) 1/24/94')
+VERSIONID(`@(#)fax.m4  8.4 (Berkeley) 10/10/95')
 
-Mfax,          P=FAX_MAILER_PATH, F=DFMhu, S=14, R=24, M=FAX_MAILER_MAX,
+Mfax,          P=FAX_MAILER_PATH, F=DFMhu, S=14, R=24, M=FAX_MAILER_MAX, T=X-Phone/X-FAX/X-Unix,
                A=mailfax $u $h $f
+
+LOCAL_CONFIG
+CPFAX
index 24a8204..124cbba 100644 (file)
@@ -38,29 +38,57 @@ ifdef(`LOCAL_MAILER_ARGS',, `define(`LOCAL_MAILER_ARGS', `mail -d $u')')
 ifdef(`LOCAL_SHELL_FLAGS',, `define(`LOCAL_SHELL_FLAGS', `eu')')
 ifdef(`LOCAL_SHELL_PATH',, `define(`LOCAL_SHELL_PATH', /bin/sh)')
 ifdef(`LOCAL_SHELL_ARGS',, `define(`LOCAL_SHELL_ARGS', `sh -c $u')')
+ifdef(`LOCAL_SHELL_DIR',, `define(`LOCAL_SHELL_DIR', `$z:/')')
 POPDIVERT
 
 ##################################################
 ###   Local and Program Mailer specification   ###
 ##################################################
 
-VERSIONID(`@(#)local.m4        8.6 (Berkeley) 10/24/93')
+VERSIONID(`@(#)local.m4        8.21 (Berkeley) 11/6/95')
 
-Mlocal,                P=LOCAL_MAILER_PATH, F=CONCAT(`lsDFM', LOCAL_MAILER_FLAGS), S=10, R=20/40,
+Mlocal,                P=LOCAL_MAILER_PATH, F=CONCAT(`lsDFMAw5:/|@', LOCAL_MAILER_FLAGS), S=10/30, R=20/40,
+               _OPTINS(`LOCAL_MAILER_MAX', `M=', `, ')_OPTINS(`LOCAL_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/X-Unix,
                A=LOCAL_MAILER_ARGS
-Mprog,         P=LOCAL_SHELL_PATH, F=CONCAT(`lsDFM', LOCAL_SHELL_FLAGS), S=10, R=20/40, D=$z:/,
+Mprog,         P=LOCAL_SHELL_PATH, F=CONCAT(`lsDFMo', LOCAL_SHELL_FLAGS), S=10/30, R=20/40, D=LOCAL_SHELL_DIR,
+               _OPTINS(`LOCAL_MAILER_MAX', `M=', `, ')T=X-Unix,
                A=LOCAL_SHELL_ARGS
 
+#
+#  Envelope sender rewriting
+#
 S10
 R<@>                   $n                      errors to mailer-daemon
-R$+                    $: $>40 $1
+R$+                    $: $>50 $1              add local domain if needed
+R$*                    $: $>94 $1              do masquerading
 
+#
+#  Envelope recipient rewriting
+#
 S20
 R$+ < @ $* >           $: $1                   strip host part
 
+#
+#  Header sender rewriting
+#
+S30
+R<@>                   $n                      errors to mailer-daemon
+R$+                    $: $>50 $1              add local domain if needed
+R$*                    $: $>93 $1              do masquerading
+
+#
+#  Header recipient rewriting
+#
 S40
-ifdef(`_ALWAYS_ADD_DOMAIN_',
-`R$* < @ $* > $*               $@ $1 < @ $2 > $3       already fully qualified
-R$*                    $: $1 @ $M              add local qualification
-R$* @                  $: $1 @ $j              if $M not defined',
-`dnl')
+R$+                    $: $>50 $1              add local domain if needed
+ifdef(`_ALL_MASQUERADE_', `', `#')dnl
+R$*                    $: $>93 $1              do all-masquerading
+
+#
+#  Common code to add local domain name (only if always-add-domain)
+#
+S50
+ifdef(`_ALWAYS_ADD_DOMAIN_', `', `#')dnl
+R$* < @ $* > $*        $@ $1 < @ $2 > $3               already fully qualified
+ifdef(`_ALWAYS_ADD_DOMAIN_', `', `#')dnl
+R$+                    $@ $1 < @ *LOCAL* >             add local qualification
diff --git a/usr.sbin/sendmail/cf/mailer/mail11.m4 b/usr.sbin/sendmail/cf/mailer/mail11.m4
new file mode 100644 (file)
index 0000000..21e05dd
--- /dev/null
@@ -0,0 +1,50 @@
+PUSHDIVERT(-1)
+#
+# Not exciting enough to bother with copyrights and most of the
+# rulesets are based from those provided by DEC.
+# Barb Dijker, Labyrinth Computer Services, barb@labyrinth.com
+#
+# This mailer is only useful if you have DECNET and the
+# mail11 program - gatekeeper.dec.com:/pub/DEC/gwtools.
+# 
+# For local delivery of DECNET style addresses to the local
+# DECNET node, you will need feature(use_cw_file) and put
+# your DECNET nodename in in the cw file.
+#
+ifdef(`MAIL11_MAILER_PATH',, `define(`MAIL11_MAILER_PATH', /usr/etc/mail11)')
+ifdef(`MAIL11_MAILER_FLAGS',, `define(`MAIL11_MAILER_FLAGS', nsFx)')
+ifdef(`MAIL11_MAILER_ARGS',, `define(`MAIL11_MAILER_ARGS', mail11 $g $x $h $u)')
+define(`_USE_DECNET_SYNTAX_')
+
+POPDIVERT
+
+PUSHDIVERT(3)
+# DECNET delivery
+R$* < @ $=w .DECNET. >         $#local $: $1                   local DECnet
+R$+ < @ $+ .DECNET. >          $#mail11 $@ $2 $: $1            DECnet user
+POPDIVERT
+
+PUSHDIVERT(6)
+CPDECNET
+POPDIVERT
+
+###########################################
+###   UTK-MAIL11 Mailer specification   ###
+###########################################
+
+VERSIONID(`@(#)mail11.m4       8.1 (Berkeley) 5/23/95')
+
+Mmail11, P=MAIL11_MAILER_PATH, F=MAIL11_MAILER_FLAGS, S=15, R=25,
+       A=MAIL11_MAILER_ARGS
+
+S15
+R$+                    $: $>25 $1              preprocess
+R$w :: $+              $@ $w :: $1             ready to go
+
+S25
+R$+ < @ $- .UUCP >     $: $2 ! $1              back to old style
+R$+ < @ $- .DECNET >   $: $2 :: $1             convert to DECnet style
+R$+ < @ $- .LOCAL >    $: $2 :: $1             convert to DECnet style
+R$+ < @ $=w. >         $: $2 :: $1             convert to DECnet style
+R$=w :: $+             $2                      strip local names
+R$+ :: $+              $@ $1 :: $2             already qualified
diff --git a/usr.sbin/sendmail/cf/mailer/phquery.m4 b/usr.sbin/sendmail/cf/mailer/phquery.m4
new file mode 100644 (file)
index 0000000..ee359e0
--- /dev/null
@@ -0,0 +1,51 @@
+PUSHDIVERT(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+#  Contributed by Kimmo Suominen <kim@tac.nyc.ny.us>.
+#
+
+ifdef(`PH_MAILER_PATH',, `define(`PH_MAILER_PATH', /usr/local/etc/phquery)')
+ifdef(`PH_MAILER_FLAGS',, `define(`PH_MAILER_FLAGS', `ehmu')')
+ifdef(`PH_MAILER_ARGS',, `define(`PH_MAILER_ARGS', `phquery -- $u')')
+
+POPDIVERT
+
+####################################
+###   PH Mailer specification   ###
+####################################
+
+VERSIONID(`@(#)phquery.m4      8.1 (Berkeley) 8/1/95')
+
+Mph,           P=PH_MAILER_PATH, F=CONCAT(`nrDFM', PH_MAILER_FLAGS), S=10, R=20/40,
+               A=PH_MAILER_ARGS
index 92bcff9..210a3c8 100644 (file)
@@ -34,7 +34,7 @@ PUSHDIVERT(-1)
 #
 
 ifdef(`POP_MAILER_PATH',, `define(`POP_MAILER_PATH', /usr/lib/mh/spop)')
-ifdef(`POP_MAILER_FLAGS',, `define(`POP_MAILER_FLAGS', `eu')')
+ifdef(`POP_MAILER_FLAGS',, `define(`POP_MAILER_FLAGS', `Penu')')
 ifdef(`POP_MAILER_ARGS',, `define(`POP_MAILER_ARGS', `pop $u')')
 
 POPDIVERT
@@ -43,9 +43,9 @@ POPDIVERT
 ###   POP Mailer specification   ###
 ####################################
 
-VERSIONID(`@(#)pop.m4  8.2 (Berkeley) 2/19/94')
+VERSIONID(`@(#)pop.m4  8.5 (Berkeley) 4/23/95')
 
-Mpop,          P=POP_MAILER_PATH, F=CONCAT(`lsDFM', POP_MAILER_FLAGS), S=10, R=20/40,
+Mpop,          P=POP_MAILER_PATH, F=CONCAT(`lsDFM', POP_MAILER_FLAGS), S=10, R=20/40, T=DNS/RFC822/X-Unix,
                A=POP_MAILER_ARGS
 
 LOCAL_CONFIG
diff --git a/usr.sbin/sendmail/cf/mailer/procmail.m4 b/usr.sbin/sendmail/cf/mailer/procmail.m4
new file mode 100644 (file)
index 0000000..d1293b9
--- /dev/null
@@ -0,0 +1,52 @@
+PUSHDIVERT(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1988, 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.
+#
+
+ifdef(`PROCMAIL_PATH',,
+       `define(`PROCMAIL_PATH', /usr/local/bin/procmail)')
+ifdef(`PROCMAIL_MAILER_FLAGS',,
+       `define(`PROCMAIL_MAILER_FLAGS', `Shu')')
+ifdef(`PROCMAIL_MAILER_ARGS',,
+       `define(`PROCMAIL_MAILER_ARGS', `procmail -m $h $f $u')')
+
+POPDIVERT
+
+######################*****##############
+###   PROCMAIL Mailer specification   ###
+##################*****##################
+
+VERSIONID(`@(#)procmail.m4     8.4 (Berkeley) 4/23/95')
+
+Mprocmail,     P=PROCMAIL_PATH, F=CONCAT(`DFMm', PROCMAIL_MAILER_FLAGS), S=11/31, R=21/31, T=DNS/RFC822/X-Unix,
+               ifdef(`PROCMAIL_MAILER_MAX', `M=PROCMAIL_MAILER_MAX, ')A=PROCMAIL_MAILER_ARGS
index 45efbd6..6b59574 100644 (file)
@@ -32,60 +32,64 @@ PUSHDIVERT(-1)
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 #
-ifdef(`SMTP_MAILER_FLAGS',,
-       `define(`SMTP_MAILER_FLAGS',
-               `ifdef(`_OLD_SENDMAIL_', `L', `')')')
+ifdef(`SMTP_MAILER_FLAGS',, `define(`SMTP_MAILER_FLAGS', `')')
+ifdef(`SMTP_MAILER_ARGS',, `define(`SMTP_MAILER_ARGS', `IPC $h')')
+ifdef(`ESMTP_MAILER_ARGS',, `define(`ESMTP_MAILER_ARGS', `IPC $h')')
+ifdef(`SMTP8_MAILER_ARGS',, `define(`SMTP8_MAILER_ARGS', `IPC $h')')
+ifdef(`RELAY_MAILER_ARGS',, `define(`RELAY_MAILER_ARGS', `IPC $h')')
+ifdef(`_MAILER_uucp_',
+       `errprint(`*** MAILER(smtp) must appear before MAILER(uucp)')')dnl
 POPDIVERT
 #####################################
 ###   SMTP Mailer specification   ###
 #####################################
 
-VERSIONID(`@(#)smtp.m4 8.15 (Berkeley) 2/14/94')
-
-Msmtp,         P=[IPC], F=CONCAT(mDFMuX, SMTP_MAILER_FLAGS), S=11/31, R=ifdef(`_ALL_MASQUERADE_', `11/31', `21'), E=\r\n,
-               ifdef(`_OLD_SENDMAIL_',, `L=990, ')ifdef(`SMTP_MAILER_MAX', `M=SMTP_MAILER_MAX, ')A=IPC $h
-Mesmtp,                P=[IPC], F=CONCAT(mDFMuXa, SMTP_MAILER_FLAGS), S=11/31, R=ifdef(`_ALL_MASQUERADE_', `11/31', `21'), E=\r\n,
-               ifdef(`_OLD_SENDMAIL_',, `L=990, ')ifdef(`SMTP_MAILER_MAX', `M=SMTP_MAILER_MAX, ')A=IPC $h
-Mrelay,                P=[IPC], F=CONCAT(mDFMuXa, SMTP_MAILER_FLAGS), S=11/31, R=61, E=\r\n,
-               ifdef(`_OLD_SENDMAIL_',, `L=2040, ')A=IPC $h
+VERSIONID(`@(#)smtp.m4 8.32 (Berkeley) 11/20/95')
+
+Msmtp,         P=[IPC], F=CONCAT(mDFMuX, SMTP_MAILER_FLAGS), S=11/31, R=ifdef(`_ALL_MASQUERADE_', `21/31', `21'), E=\r\n, L=990,
+               _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,
+               A=SMTP_MAILER_ARGS
+Mesmtp,                P=[IPC], F=CONCAT(mDFMuXa, SMTP_MAILER_FLAGS), S=11/31, R=ifdef(`_ALL_MASQUERADE_', `21/31', `21'), E=\r\n, L=990,
+               _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,
+               A=ESMTP_MAILER_ARGS
+Msmtp8,                P=[IPC], F=CONCAT(mDFMuX8, SMTP_MAILER_FLAGS), S=11/31, R=ifdef(`_ALL_MASQUERADE_', `21/31', `21'), E=\r\n, L=990,
+               _OPTINS(`SMTP_MAILER_MAX', `M=', `, ')_OPTINS(`SMTP_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,
+               A=SMTP8_MAILER_ARGS
+Mrelay,                P=[IPC], F=CONCAT(mDFMuXa8, SMTP_MAILER_FLAGS), S=11/31, R=ifdef(`_ALL_MASQUERADE_', `61/71', `61'), E=\r\n, L=2040,
+               _OPTINS(`RELAY_MAILER_CHARSET', `C=', `, ')T=DNS/RFC822/SMTP,
+               A=RELAY_MAILER_ARGS
 
 #
-#  envelope sender and masquerading recipient rewriting
+#  envelope sender rewriting
 #
 S11
 R$+                    $: $>51 $1                      sender/recipient common
-R$* :; <@>             $@ $1 :;                        list:; special case
-R$*                    $@ $>61 $1                      qualify unqual'ed names
+R$* :; <@>             $@                              list:; special case
+R$*                    $: $>61 $1                      qualify unqual'ed names
+R$+                    $: $>94 $1                      do masquerading
 
 
 #
-#  header recipient rewriting if not masquerading recipients
+#  envelope recipient rewriting --
+#  also header recipient if not masquerading recipients
 #
 S21
-
-# do sender/recipient common rewriting
-R$+                    $: $>51 $1
-
-# unqualified names (e.g., "eric") are qualified by local host
-R$* < @ $* > $*                $@ $1 < @ $2 > $3               already qualified
-R$+                    $: $1 < @ $j >                  add local domain
+R$+                    $: $>51 $1                      sender/recipient common
+R$+                    $: $>61 $1                      qualify unqual'ed names
 
 
 #
-#  header sender and masquerading recipient rewriting
+#  header sender and masquerading header recipient rewriting
 #
 S31
 R$+                    $: $>51 $1                      sender/recipient common
-R$* :; <@>             $@ $1 :;                        list:; special case
+R:; <@>                        $@                              list:; special case
 
 # do special header rewriting
 R$* <@> $*             $@ $1 <@> $2                    pass null host through
 R< @ $* > $*           $@ < @ $1 > $2                  pass route-addr through
-R$=E < @ $=w . >       $@ $1 < @ $2 >                  exposed user as is
-R$* < @ $=w . >                $: $1 < @ $2 @ $M >             masquerade as domain
-R$* < @ $+ @ >         $@ $1 < @ $2 >                  in case $M undefined
-R$* < @ $+ @ $+ >      $@ $1 < @ $3 >                  $M is defined -- use it
-R$*                    $@ $>61 $1                      qualify unqual'ed names
+R$*                    $: $>61 $1                      qualify unqual'ed names
+R$+                    $: $>93 $1                      do masquerading
 
 
 #
@@ -110,9 +114,7 @@ R$+ < @ $* > $*             $@ $1 < @ $2 > $3               not UUCP form
 R< $&h ! > $- ! $+     $@ $2 < @ $1 .UUCP. >
 R< $&h ! > $-.$+ ! $+  $@ $3 < @ $1.$2 >
 R< $&h ! > $+          $@ $1 < @ $&h .UUCP. >
-R< $+ ! > $+           $: $1 ! $2 < @ $Y >
-R$+ < @ >              $: $1 < @ $j >                  in case $Y undefined
-R$+ < @ $+ : $+ >      $: $1 < @ $3 >                  strip mailer: part')
+R< $+ ! > $+           $: $1 ! $2 < @ *LOCAL* >')
 
 
 #
@@ -120,7 +122,14 @@ R$+ < @ $+ : $+ >  $: $1 < @ $3 >                  strip mailer: part')
 #
 S61
 
-R$* < @ $* > $*                $@ $1 < @ $2 > $3               already qualified
-R$=E                   $@ $1 < @ $j>                   show exposed names
-R$+                    $: $1 < @ $M >                  user w/o host
-R$+ <@>                        $: $1 < @ $j >                  in case $M undefined
+R$* < @ $* > $*                $@ $1 < @ $2 > $3               already fully qualified
+R$+                    $@ $1 < @ *LOCAL* >             add local qualification
+
+
+#
+#  relay mailer header masquerading recipient rewriting
+#
+S71
+
+R$+                    $: $>61 $1
+R$+                    $: $>93 $1
index fe6ee30..2abf3b0 100644 (file)
@@ -41,7 +41,8 @@ POPDIVERT
 ###  USENET Mailer specification ###
 ####################################
 
-VERSIONID(`@(#)usenet.m4       8.3 (Berkeley) 1/24/94')
+VERSIONID(`@(#)usenet.m4       8.5 (Berkeley) 4/26/95')
 
-Musenet,       P=USENET_MAILER_PATH, F=USENET_MAILER_FLAGS, S=10, R=20,ifdef(`USENET_MAILER_MAX', ` M=USENET_MAILER_MAX,')
+Musenet,       P=USENET_MAILER_PATH, F=USENET_MAILER_FLAGS, S=10, R=20,
+               _OPTINS(`USENET_MAILER_MAX', `M=', `, ')T=X-Usenet/X-Usenet/X-Unix,
                A=USENET_MAILER_ARGS $u
index dd7aa87..0b65949 100644 (file)
@@ -34,15 +34,17 @@ PUSHDIVERT(-1)
 #
 
 ifdef(`UUCP_MAILER_PATH',, `define(`UUCP_MAILER_PATH', /usr/bin/uux)')
-ifdef(`UUCP_MAILER_ARGS',, `define(`UUCP_MAILER_ARGS', `uux - -r -a$f -gC $h!rmail ($u)')')
+ifdef(`UUCP_MAILER_ARGS',, `define(`UUCP_MAILER_ARGS', `uux - -r -a$g -gC $h!rmail ($u)')')
 ifdef(`UUCP_MAILER_FLAGS',, `define(`UUCP_MAILER_FLAGS', `')')
-ifdef(`UUCP_MAX_SIZE',, `define(`UUCP_MAX_SIZE', 100000)')
+ifdef(`UUCP_MAILER_MAX',,
+       `define(`UUCP_MAILER_MAX',
+               `ifdef(`UUCP_MAX_SIZE', `UUCP_MAX_SIZE', 100000)')')
 POPDIVERT
 #####################################
 ###   UUCP Mailer specification   ###
 #####################################
 
-VERSIONID(`@(#)uucp.m4 8.16 (Berkeley) 4/14/94')
+VERSIONID(`@(#)uucp.m4 8.24 (Berkeley) 9/5/95')
 
 #
 #  There are innumerable variations on the UUCP mailer.  It really
@@ -50,24 +52,30 @@ VERSIONID(`@(#)uucp.m4      8.16 (Berkeley) 4/14/94')
 #
 
 # old UUCP mailer (two names)
-Muucp,         P=UUCP_MAILER_PATH, F=CONCAT(DFMhuU, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAX_SIZE,
+Muucp,         P=UUCP_MAILER_PATH, F=CONCAT(DFMhuUd, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAILER_MAX,
+               _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,
                A=UUCP_MAILER_ARGS
-Muucp-old,     P=UUCP_MAILER_PATH, F=CONCAT(DFMhuU, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAX_SIZE,
+Muucp-old,     P=UUCP_MAILER_PATH, F=CONCAT(DFMhuUd, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAILER_MAX,
+               _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,
                A=UUCP_MAILER_ARGS
 
 # smart UUCP mailer (handles multiple addresses) (two names)
-Msuucp,                P=UUCP_MAILER_PATH, F=CONCAT(mDFMhuU, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAX_SIZE,
+Msuucp,                P=UUCP_MAILER_PATH, F=CONCAT(mDFMhuUd, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAILER_MAX,
+               _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,
                A=UUCP_MAILER_ARGS
-Muucp-new,     P=UUCP_MAILER_PATH, F=CONCAT(mDFMhuU, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAX_SIZE,
+Muucp-new,     P=UUCP_MAILER_PATH, F=CONCAT(mDFMhuUd, UUCP_MAILER_FLAGS), S=12, R=22/42, M=UUCP_MAILER_MAX,
+               _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,
                A=UUCP_MAILER_ARGS
 
 ifdef(`_MAILER_smtp_',
 `# domain-ized UUCP mailer
-Muucp-dom,     P=UUCP_MAILER_PATH, F=CONCAT(mDFMhu, UUCP_MAILER_FLAGS), S=52/31, R=ifdef(`_ALL_MASQUERADE_', `11/31', `21'), M=UUCP_MAX_SIZE,
+Muucp-dom,     P=UUCP_MAILER_PATH, F=CONCAT(mDFMhud, UUCP_MAILER_FLAGS), S=52/31, R=ifdef(`_ALL_MASQUERADE_', `21/31', `21'), M=UUCP_MAILER_MAX,
+               _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,
                A=UUCP_MAILER_ARGS
 
 # domain-ized UUCP mailer with UUCP-style sender envelope
-Muucp-uudom,   P=UUCP_MAILER_PATH, F=CONCAT(mDFMhu, UUCP_MAILER_FLAGS), S=72/31, R=ifdef(`_ALL_MASQUERADE_', `11/31', `21'), M=UUCP_MAX_SIZE,
+Muucp-uudom,   P=UUCP_MAILER_PATH, F=CONCAT(mDFMhud, UUCP_MAILER_FLAGS), S=72/31, R=ifdef(`_ALL_MASQUERADE_', `21/31', `21'), M=UUCP_MAILER_MAX,
+               _OPTINS(`UUCP_MAILER_CHARSET', `C=', `, ')T=X-UUCP/X-UUCP/X-Unix,
                A=UUCP_MAILER_ARGS')
 
 
@@ -79,10 +87,10 @@ S12
 # handle error address as a special case
 R<@>                           $n                      errors to mailer-daemon
 
-# do not qualify list:; syntax
-R$* :; <@>                     $@ $1 :;
+# list:; syntax should disappear
+R:; <@>                                $@
 
-R$* < @ $* . >                 $1 < @ $2 >             strip trailing dots
+R$* < @ $* . > $*              $1 < @ $2 > $3          strip trailing dots
 R$* < @ $=w >                  $1                      strip local name
 R<@ $- . UUCP > : $+           $1 ! $2                 convert to UUCP format
 R<@ $+ > : $+                  $1 ! $2                 convert to UUCP format
@@ -98,10 +106,10 @@ R! $+                              $: $k ! $1              in case $U undefined
 #
 S22
 
-# don't touch list:; syntax
-R$* :; <@>                     $@ $1 :;
+# list:; should disappear
+R:; <@>                                $@
 
-R$* < @ $* . >                 $1 < @ $2 >             strip trailing dots
+R$* < @ $* . > $*              $1 < @ $2 > $3          strip trailing dots
 R$* < @ $j >                   $1                      strip local name
 R<@ $- . UUCP > : $+           $1 ! $2                 convert to UUCP format
 R<@ $+ > : $+                  $1 ! $2                 convert to UUCP format
@@ -113,10 +121,10 @@ R$* < @ $+ >                      $2 ! $1                 convert to UUCP format
 #
 S42
 
-# don't touch list:; syntax
-R$* :; <@>                     $@ $1 :;
+# list:; syntax should disappear
+R:; <@>                                $@
 
-R$* < @ $* . >                 $1 < @ $2 >             strip trailing dots
+R$* < @ $* . > $*              $1 < @ $2 > $3          strip trailing dots
 R$* < @ $j >                   $1                      strip local name
 R<@ $- . UUCP > : $+           $1 ! $2                 convert to UUCP format
 R<@ $+ > : $+                  $1 ! $2                 convert to UUCP format
@@ -148,20 +156,14 @@ S72
 # handle error address as a special case
 R<@>                           $n                      errors to mailer-daemon
 
-# do not qualify list:; syntax
-R$* :; <@>                     $@ $1 :;
+# do standard SMTP mailer rewriting
+R$*                            $: $>11 $1
 
-R$* < @ $* . >                 $1 < @ $2 >             strip trailing dots
-R$* < @ $=w >                  $1                      strip local name
-R<@ $- . UUCP > : $+           $1 ! $2                 convert to UUCP format
-R<@ $+ > : $+                  $1 ! $2                 convert to UUCP format
-R$* < @ $- . UUCP >            $2 ! $1                 convert to UUCP format
-R$* < @ $+ >                   $@ $2 ! $1              convert to UUCP format
-
-R$&h ! $+ ! $+                 $@ $1 ! $2              $h!...!user => ...!user
-R$&h ! $+                      $@ $&h ! $1             $h!user => $h!user
-R$+                            $: $M ! $1              prepend masquerade name
-R! $+                          $: $j ! $1              in case $M undefined')
+R$* < @ $* . > $*              $1 < @ $2 > $3          strip trailing dots
+R<@ $- . UUCP > : $+           $@ $1 ! $2              convert to UUCP format
+R<@ $+ > : $+                  $@ $1 ! $2              convert to UUCP format
+R$* < @ $- . UUCP >            $@ $2 ! $1              convert to UUCP format
+R$* < @ $+ >                   $@ $2 ! $1              convert to UUCP format')
 
 
 PUSHDIVERT(4)
index 1950528..835e4d8 100644 (file)
@@ -35,8 +35,8 @@ divert(-1)
 #
 
 divert(0)
-VERSIONID(`@(#)bsd4.4.m4       8.2 (Berkeley) 2/10/94')
-define(`HELP_FILE', /usr/share/misc/sendmail.hf)dnl
-define(`STATUS_FILE', /var/log/sendmail.st)dnl
-define(`LOCAL_MAILER_PATH', /usr/libexec/mail.local)dnl
-define(`UUCP_MAILER_ARGS', `uux - -r -z -a$f $h!rmail ($u)')dnl
+VERSIONID(`@(#)bsd4.4.m4       8.4 (Berkeley) 11/13/95')
+ifdef(`HELP_FILE',, `define(`HELP_FILE', /usr/share/misc/sendmail.hf)')dnl
+ifdef(`STATUS_FILE',, `define(`STATUS_FILE', /var/log/sendmail.st)')dnl
+ifdef(`LOCAL_MAILER_PATH',, `define(`LOCAL_MAILER_PATH', /usr/libexec/mail.local)')dnl
+ifdef(`UUCP_MAILER_ARGS',, `define(`UUCP_MAILER_ARGS', `uux - -r -z -a$g $h!rmail ($u)')')dnl
index dd5044a..68b85d3 100644 (file)
@@ -32,7 +32,7 @@
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 #
-#      @(#)makeinfo.sh 8.4 (Berkeley) 3/4/94
+#      @(#)makeinfo.sh 8.6 (Berkeley) 8/6/95
 #
 
 usewhoami=0
@@ -75,3 +75,5 @@ else
 fi
 echo '#####' built by $user@$host on `date`
 echo '#####' in `pwd` | sed 's/\/tmp_mnt//'
+echo '#####' using $1 as configuration include directory | sed 's/\/tmp_mnt//'
+echo "define(\`__HOST__', $host)dnl"
index 4fad761..52d6d21 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * By John G. Myers, jgm+@cmu.edu
- * Version 1.1
+ * Version 1.2
  *
  * Process a BITNET "internet.listing" file, producing output
  * suitable for input to makemap.
@@ -154,7 +154,7 @@ char *domainlen;
             * if "domain" doesn't have a domain expansion already.
             */
            p = lookup(domain);
-           if (!p || !index(p, '.')) remember(domain, otherdomain);
+           if (!p || !strchr(p, '.')) remember(domain, otherdomain);
        }
     }
     else {
@@ -167,7 +167,7 @@ char *domainlen;
                 * have a domain expansion, give it the expansion "domain".
                 */
                p = lookup(otherdomain);
-               if (!p || !index(p, '.')) remember(otherdomain, domain);
+               if (!p || !strchr(p, '.')) remember(otherdomain, domain);
            }
        }
        else {
diff --git a/usr.sbin/sendmail/contrib/bsdi.mc b/usr.sbin/sendmail/contrib/bsdi.mc
new file mode 100644 (file)
index 0000000..7c7553f
--- /dev/null
@@ -0,0 +1,191 @@
+Return-Path: sanders@austin.BSDI.COM
+Received: from hofmann.CS.Berkeley.EDU (hofmann.CS.Berkeley.EDU [128.32.34.35]) by orodruin.CS.Berkeley.EDU (8.6.9/8.7.0.Beta0) with ESMTP id KAA28278 for <eric@orodruin.CS.Berkeley.EDU>; Sat, 10 Dec 1994 10:49:08 -0800
+Received: from austin.BSDI.COM (austin.BSDI.COM [137.39.95.2]) by hofmann.CS.Berkeley.EDU (8.6.9/8.6.6.Beta11) with ESMTP id KAA09482 for <eric@cs.berkeley.edu>; Sat, 10 Dec 1994 10:49:03 -0800
+Received: from austin.BSDI.COM (sanders@localhost [127.0.0.1]) by austin.BSDI.COM (8.6.9/8.6.9) with ESMTP id MAA14919 for <eric@cs.berkeley.edu>; Sat, 10 Dec 1994 12:49:01 -0600
+Message-Id: <199412101849.MAA14919@austin.BSDI.COM>
+To: Eric Allman <eric@cs.berkeley.edu>
+Subject: Re: sorting mailings lists with fastest delivery users first 
+In-reply-to: Your message of Sat, 10 Dec 1994 08:25:30 PST.
+References: <199412101625.IAA15407@mastodon.CS.Berkeley.EDU> 
+From: Tony Sanders <sanders@bsdi.com>
+Organization: Berkeley Software Design, Inc.
+Date: Sat, 10 Dec 1994 12:49:00 -0600
+Sender: sanders@austin.BSDI.COM
+
+(some random text deleted)
+
+I'll send you something else I've hacked up.  You are free to use this
+or do with it as you like (I hereby make all my parts public domain).
+It's a sample .mc file that has comments (mostly taken from the README)
+and examples describing most of the common things people need to setup.
+
+#
+# /usr/share/sendmail/cf/sample.mc
+#
+# Do not edit /etc/sendmail.cf directly unless you cannot do what you
+# want in the master config file (/usr/share/sendmail/cf/sample.mc).
+# To create /etc/sendmail.cf from the master:
+#     cd /usr/share/sendmail/cf
+#     mv /etc/sendmail.cf /etc/sendmail.cf.save
+#     m4 < sample.mc > /etc/sendmail.cf
+#
+# Then kill and restart sendmail:
+#     sh -c 'set `cat /var/run/sendmail.pid`; kill $1; shift; eval "$@"'
+#
+# See /usr/share/sendmail/README for help in building a configuration file.
+#
+include(`../m4/cf.m4')
+VERSIONID(`@(#)$Id: bsdi.mc,v 1.1 1996/01/29 01:42:36 dm Exp $')
+
+dnl # Specify your OS type below
+OSTYPE(`bsd4.4')
+
+dnl # NOTE: `dnl' is the m4 command for delete-to-newline; these are
+dnl # used to prevent those lines from appearing in the sendmail.cf.
+dnl #
+dnl # UUCP-only sites should configure FEATURE(`nodns') and SMART_HOST.
+dnl # The uucp-dom mailer requires MAILER(smtp).  For more info, see
+dnl # `UUCP Config' at the end of this file.
+
+dnl # If you are not running DNS at all, it is important to use
+dnl # FEATURE(nodns) to avoid having sendmail queue everything
+dnl # waiting for the name server to come up.
+dnl # Example:
+dnl     FEATURE(`nodns')
+
+dnl # Use FEATURE(`nocanonify') to skip address canonification via $[ ... $].
+dnl # This would generally only be used by sites that only act as mail gateways
+dnl # or which have user agents that do full canonification themselves.
+dnl # You may also want to use:
+dnl #     define(`confBIND_OPTS',`-DNSRCH -DEFNAMES')
+dnl # to turn off the usual resolver options that do a similar thing.
+dnl # Examples:
+dnl     FEATURE(`nocanonify')
+dnl     define(`confBIND_OPTS',`-DNSRCH -DEFNAMES')
+
+dnl # If /bin/hostname is not set to the FQDN (Full Qualified Domain Name;
+dnl # for example, foo.bar.com) *and* you are not running a nameserver
+dnl # (that is, you do not have an /etc/resolv.conf and are not running
+dnl # named) *and* the canonical name for your machine in /etc/hosts
+dnl # (the canonical name is the first name listed for a given IP Address)
+dnl # is not the FQDN version then define NEED_DOMAIN and specify your
+dnl # domain using `DD' (for example, if your hostname is `foo.bar.com'
+dnl # then use DDbar.com).  If in doubt, just define it anyway; doesn't hurt.
+dnl # Examples:
+dnl     define(`NEED_DOMAIN', `1')
+dnl     DDyour.site.domain
+
+dnl # Define SMART_HOST if you want all outgoing mail to go to a central
+dnl # site.  SMART_HOST applies to names qualified with non-local names.
+dnl # Example:
+dnl     define(`SMART_HOST', `smtp:firewall.bar.com')
+
+dnl # Define MAIL_HUB if you want all incoming mail sent to a
+dnl # centralized hub, as for a shared /var/spool/mail scheme.
+dnl # MAIL_HUB applies to names qualified with the name of the
+dnl # local host (e.g., "eric@foo.bar.com").
+dnl # Example:
+dnl     define(`MAIL_HUB', `smtp:mailhub.bar.com')
+
+dnl # LOCAL_RELAY is a site that will handle unqualified names, this is
+dnl # basically for site/company/department wide alias forwarding.  By
+dnl # default mail is delivered on the local host.
+dnl # Example:
+dnl     define(`LOCAL_RELAY', `smtp:mailgate.bar.com')
+
+dnl # Relay hosts for fake domains: .UUCP .BITNET .CSNET
+dnl # Examples:
+dnl     define(`UUCP_RELAY', `mailer:your_relay_host')
+dnl     define(`BITNET_RELAY', `mailer:your_relay_host')
+dnl     define(`CSNET_RELAY', `mailer:your_relay_host')
+
+dnl # Define `MASQUERADE_AS' is used to hide behind a gateway.
+dnl # add any accounts you wish to be exposed (i.e., not hidden) to the
+dnl # `EXPOSED_USER' list.
+dnl # Example:
+dnl     MASQUERADE_AS(`some.other.host')
+
+dnl # If masquerading, EXPOSED_USER defines the list of accounts
+dnl # that retain the local hostname in their address.
+dnl # Example:
+dnl     EXPOSED_USER(`postmaster hostmaster webmaster')
+
+dnl # If masquerading is enabled (using MASQUERADE_AS above) then
+dnl # FEATURE(allmasquerade) will cause recipient addresses to
+dnl # masquerade as being from the masquerade host instead of
+dnl # getting the local hostname.  Although this may be right for
+dnl # ordinary users, it breaks local aliases that aren't exposed
+dnl # using EXPOSED_USER.
+dnl # Example:
+dnl     FEATURE(allmasquerade)
+
+dnl # Include any required mailers
+MAILER(local)
+MAILER(smtp)
+MAILER(uucp)
+
+LOCAL_CONFIG
+# If this machine should be accepting mail as local for other hostnames
+# that are MXed to this hostname then add those hostnames below using
+# a line like:
+#     Cw bar.com
+# The most common case where you need this is if this machine is supposed
+# to be accepting mail for the domain.  That is, if this machine is
+# foo.bar.com and you have an MX record in the DNS that looks like:
+#     bar.com.  IN      MX      0 foo.bar.com.
+# Then you will need to add `Cw bar.com' to the config file for foo.bar.com.
+# DO NOT add Cw entries for hosts whom you simply store and forward mail
+# for or else it will attempt local delivery.  So just because bubba.bar.com
+# is MXed to your machine you should not add a `Cw bubba.bar.com' entry
+# unless you want local delivery and your machine is the highest-priority
+# MX entry (that is is has the lowest preference value in the DNS.
+
+LOCAL_RULE_0
+# `LOCAL_RULE_0' can be used to introduce alternate delivery rules.
+# For example, let's say you accept mail via an MX record for widgets.com
+# (don't forget to add widgets.com to your Cw list, as above).
+#
+# If wigets.com only has an AOL address (widgetsinc) then you could use:
+# R$+ <@ widgets.com.>  $#smtp $@aol.com. $:widgetsinc<@aol.com.>
+#
+# Or, if widgets.com was connected to you via UUCP as the UUCP host
+# widgets you might have:
+# R$+ <@ widgets.com.>   $#uucp $@widgets $:$1<@widgets.com.>
+
+dnl ###
+dnl ### UUCP Config
+dnl ###
+
+dnl # `SITECONFIG(site_config_file, name_of_site, connection)'
+dnl # site_config_file the name of a file in the cf/siteconfig
+dnl #                  directory (less the `.m4')
+dnl # name_of_site     the actual name of your UUCP site
+dnl # connection       one of U, W, X, or Y; where U means the sites listed
+dnl #                  in the config file are connected locally;  W, X, and Y
+dnl #                  build remote UUCP hub classes ($=W, etc).
+dnl # You will need to create the specific site_config_file in
+dnl #     /usr/share/sendmail/siteconfig/site_config_file.m4
+dnl # The site_config_file contains a list of directly connected UUCP hosts,
+dnl # e.g., if you only connect to UUCP site gargoyle then you could just:
+dnl #   echo 'SITE(gargoyle)' > /usr/share/sendmail/siteconfig/uucp.foobar.m4
+dnl # Example:
+dnl     SITECONFIG(`uucp.foobar', `foobar', U)
+
+dnl # If you are on a local SMTP-based net that connects to the outside
+dnl # world via UUCP, you can use LOCAL_NET_CONFIG to add appropriate rules.
+dnl # For example:
+dnl #   define(`SMART_HOST', suucp:uunet)
+dnl #   LOCAL_NET_CONFIG
+dnl #   R$* < @ $* .$m. > $*    $#smtp $@ $2.$m. $: $1 < @ $2.$m. > $3
+dnl # This will cause all names that end in your domain name ($m) to be sent
+dnl # via SMTP; anything else will be sent via suucp (smart UUCP) to uunet.
+dnl # If you have FEATURE(nocanonify), you may need to omit the dots after
+dnl # the $m.
+dnl #
+dnl # If you are running a local DNS inside your domain which is not
+dnl # otherwise connected to the outside world, you probably want to use:
+dnl #   define(`SMART_HOST', smtp:fire.wall.com)
+dnl #   LOCAL_NET_CONFIG
+dnl #   R$* < @ $* . > $*       $#smtp $@ $2. $: $1 < @ $2. > $3
+dnl # That is, send directly only to things you found in your DNS lookup;
+dnl # anything else goes through SMART_HOST.
index a08f8d7..eec3b9a 100644 (file)
@@ -1,14 +1,13 @@
-#!/usr/local/bin/perl
+#!/usr/bin/perl
 'di ';
 'ds 00 \\"';
-'ig00 ';
+'ig 00 ';
 #
 #       THIS PROGRAM IS ITS OWN MANUAL PAGE.  INSTALL IN man & bin.
 #
 
 # hardcoded constants, should work fine for BSD-based systems
-$AF_INET = 2;
-$SOCK_STREAM = 1;
+require 'sys/socket.ph';
 $sockaddr = 'S n a4 x8';
 
 # system requirements:
@@ -70,7 +69,7 @@ $sockaddr = 'S n a4 x8';
 #      ask each server in turn a whole bunch of questions, addresses to
 #      be expanded are queued up.
 #
-#      This means that all account w.r.t. an address must be stored in
+#      This means that all accounting w.r.t. an address must be stored in
 #      various arrays.  Generally these arrays are indexed by the
 #      string "$addr *** $server" where $addr is the address to be
 #      expanded "foo" or maybe "foo@bar" and $server is the hostname
@@ -102,13 +101,18 @@ $sockaddr = 'S n a4 x8';
 $have_nslookup = 1;    # we have the nslookup program
 $port = 'smtp';
 $av0 = $0;
-$0 = "$av0 - running hostname";
 $ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,;
-chop($hostname = `hostname`);
+$ENV{'PATH'} .= ":/usr/ucb" unless $ENV{'PATH'} =~ m,/usr/ucb,;
 select(STDERR);
 
-$usage = "Usage: $av0 [-1avwd] user[@host] [user2[host2] ...]";
+$0 = "$av0 - running hostname";
+chop($name = `hostname || uname -n`);
+
+$0 = "$av0 - lookup host FQDN and IP addr";
+($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name);
+
 $0 = "$av0 - parsing args";
+$usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]";
 for $a (@ARGV) {
        die $usage if $a eq "-";
        while ($a =~ s/^(-.*)([1avwd])/$1/) {
@@ -143,8 +147,7 @@ $0 = "$av0 - building local socket";
 ($name,$aliases,$proto) = getprotobyname('tcp');
 ($name,$aliases,$port) = getservbyname($port,'tcp')
        unless $port =~ /^\d+/;
-($name,$aliases,$type,$len,$thisaddr) = gethostbyname($hostname);
-$this = pack($sockaddr, $AF_INET, 0, $thisaddr);
+$this = pack($sockaddr, &AF_INET, 0, $thisaddr);
 
 HOST:
 while (@hosts) {
@@ -174,8 +177,8 @@ while (@hosts) {
                                
        # get a connection, or look for an mx
        $0 = "$av0 - socket to $server";
-       $that = pack($sockaddr, $AF_INET, $port, $thataddr);
-       socket(S, $AF_INET, $SOCK_STREAM, $proto)
+       $that = pack($sockaddr, &AF_INET, $port, $thataddr);
+       socket(S, &AF_INET, &SOCK_STREAM, $proto)
                || die "socket: $!";
        $0 = "$av0 - bind to $server";
        bind(S, $this) 
@@ -200,8 +203,8 @@ while (@hosts) {
                print if $watch;
                if (/^(\d+)([- ])/) {
                        if ($1 != 220) {
-                               $0 = "$av0 - bad numeric responce from $server";
-                               &alarm("giving up after bet responce from $server",'');
+                               $0 = "$av0 - bad numeric response from $server";
+                               &alarm("giving up after bad response from $server",'');
                                &read_response($2,$watch);
                                alarm(0);
                                print STDERR "$server: NOT 220 greeting: $_"
@@ -213,7 +216,7 @@ while (@hosts) {
                        }
                        last if ($2 eq " ");
                } else {
-                       $0 = "$av0 - bad responce from $server";
+                       $0 = "$av0 - bad response from $server";
                        print STDERR "$server: NOT 220 greeting: $_"
                                if ($debug || $vw);
                        unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) {
@@ -261,7 +264,9 @@ while (@hosts) {
                        @toExpn = ();
                }
 
-               ($ecode,@expansion) = &expn_vrfy($u,$server);
+#              ($ecode,@expansion) = &expn_vrfy($u,$server);
+               (@foo) = &expn_vrfy($u,$server);
+               ($ecode,@expansion) = @foo;
                if ($ecode) {
                        &giveup('',$ecode,$u);
                        last USER;
@@ -329,7 +334,7 @@ while (@hosts) {
                        # 550 is a known code...  Should the be
                        # included in -a output?  Might be a bug
                        # here.  Does it matter?  Can assume that
-                       # there won't be UNKNOWN USER responces 
+                       # there won't be UNKNOWN USER responses 
                        # mixed with valid users?
                        if ($s =~ /^(550)([- ])/) {
                                if ($valid) {
@@ -414,6 +419,8 @@ sub giveup
 {
        local($redirect_okay,$reason,$user) = @_;
        local($us,@so,$nh,@remaining_users);
+       local($pk,$file,$line);
+       ($pk, $file, $line) = caller;
 
        $0 = "$av0 - giving up on $server: $reason";
        #
@@ -427,7 +434,7 @@ sub giveup
                $giveup{$server} = $reason;
                print STDERR "$reason\n";
        }
-       print "Giveup!!! redirect okay = $redirect_okay; $reason\n" if $debug;
+       print "Giveup at $file:$line!!! redirect okay = $redirect_okay; $reason\n" if $debug;
        #
        # Wait!
        # Before giving up, see if there is a chance that
@@ -486,7 +493,7 @@ sub try_fallback
        if (defined $fellback{$us}) {
                #
                # Undo a previous fallback so that we can try again
-               # Nest fallbacks are avoided because they could
+               # Nested fallbacks are avoided because they could
                # lead to infinite loops
                #
                $fallhost = $fellback{$us};
@@ -579,7 +586,7 @@ sub do_validAddr
 #
 # Ranking of inputs: best: user@host.domain, okay: user
 #
-# Return value: $error_string, @responces_from_server
+# Return value: $error_string, @responses_from_server
 sub expn_vrfy
 {
        local($u,$server) = @_;
@@ -594,7 +601,7 @@ sub expn_vrfy
        TRY:
        for $c (@c) {
                for $try_u (@try_u) {
-                       &alarm("$c'ing $try_u on $server",'',$u);
+                       &alarm("${c}'ing $try_u on $server",'',$u);
                        &ps("$c $try_u");
                        alarm(0);
                        $s = <S>;
@@ -608,7 +615,7 @@ sub expn_vrfy
                                $code = 250;
                                @ret = ("",$s);
                                push(@ret,&read_response($2,$debug));
-                               return @ret;
+                               return (@ret);
                        } 
                        if ($1 == 551 || $1 == 251) {
                                $code = $1;
@@ -648,7 +655,7 @@ sub parse
 }
 
 # returns ($new_smtp_server,$new_address,$new_name)
-# given a responce from a SMTP server ($newaddr), the 
+# given a response from a SMTP server ($newaddr), the 
 # current host ($server), the old "name" and a flag that
 # indicates if it is being called during the initial 
 # command line parsing ($parsing_args)
@@ -672,13 +679,13 @@ sub parse2
        #
        if ($newaddr =~ /^\<(.*)\>$/) {
                print "<A:$1>\n" if $debug;
-               $newaddr = &trim($1);
+               ($newaddr) = &trim($1);
                print "na = $newaddr\n" if $debug;
        }
        if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) {
                # address has a < > pair in it.
                print "N:$1 <A:$2> N:$3\n" if $debug;
-               $newaddr = &trim($2);
+               ($newaddr) = &trim($2);
                unshift(@names, &trim($3,$1));
                print "na = $newaddr\n" if $debug;
        }
@@ -754,7 +761,7 @@ sub trim
 }
 # using the host part of an address, and the server name, add the
 # servers' domain to the address if it doesn't already have a 
-# domain.  Since this sometimes failes, save a back reference so
+# domain.  Since this sometimes fails, save a back reference so
 # it can be unrolled.
 sub domainify
 {
@@ -1047,11 +1054,6 @@ sub mxlookup
 
        # provide fallbacks in case $nserver doesn't work out
        if (defined $fallback{$cpref}) {
-#              for $u (@users) {
-#                      print "mx_secondary{$u *** $nserver} = ".$fallback{$cpref}."\n"
-#                              if $debug;
-#                      $mx_secondary{"$u *** $nserver"} = $fallback{$cpref};
-#              }
                $mx_secondary{$server} = $fallback{$cpref};
        }
 
@@ -1191,15 +1193,15 @@ sub alarm
        alarm(3600);
        $SIG{ALRM} = 'handle_alarm';
 }
-# this involves one GREAT hack.
-# the "next HOST" has to unwind the stack!
+# this involves one great big ugly hack.
+# the "next HOST" unwinds the stack!
 sub handle_alarm
 {
        &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user);
        next HOST;
 }
 
-# read the rest of the current smtp daemon's responce (and toss it away)
+# read the rest of the current smtp daemon's response (and toss it away)
 sub read_response
 {
        local($done,$watch) = @_;
@@ -1227,11 +1229,11 @@ $flag_1;
 %already_mx_fellback;
 &handle_alarm;
 ################### BEGIN PERL/TROFF TRANSITION 
-.00  
+.00 ;  
 
-'di            \\ " finish diversion--previous line must be blank
-.nr nl 0-1     \\ " fake up transition to first page again
-.nr % 0                \\ " start at page 1
+'di
+.nr nl 0-1
+.nr % 0
 .\\"'; __END__ 
 .\" ############## END PERL/TROFF TRANSITION
 .TH EXPN 1 "March 11, 1993"
@@ -1359,7 +1361,7 @@ and Jon Broome has dropped off the face of the earth!
 .SH AVAILABILITY
 The latest version of 
 .B expn
-is available through anonymous ftp to
-.IR idiom.berkeley.ca.us .
+is available through anonymous ftp at
+.IR ftp://ftp.idiom.com/pub/muir-programs/expn .
 .SH AUTHOR
-.I David Muir Sharnoff\ \ \ \ <muir@idiom.berkeley.ca.us>
+.I David Muir Sharnoff\ \ \ \ <muir@idiom.com>
diff --git a/usr.sbin/sendmail/contrib/mailprio b/usr.sbin/sendmail/contrib/mailprio
new file mode 100644 (file)
index 0000000..cdbc3b0
--- /dev/null
@@ -0,0 +1,297 @@
+Message-Id: <199412081919.NAA23234@austin.BSDI.COM>
+To: Eric Allman <eric@cs.berkeley.edu>
+Subject: Re: sorting mailings lists with fastest delivery users first 
+In-reply-to: Your message of Thu, 08 Dec 1994 06:08:33 PST.
+References: <199412081408.GAA06210@mastodon.CS.Berkeley.EDU> 
+From: Tony Sanders <sanders@bsdi.com>
+Organization: Berkeley Software Design, Inc.
+Date: Thu, 08 Dec 1994 13:19:39 -0600
+Sender: sanders@austin.BSDI.COM
+
+Eric Allman writes:
+> Nope, that's a new one, so far as I know.  Any interest in
+> contributing it?  For small lists it seems overkill, but for
+> large lists it could be a major win.
+
+Sure, I will contribute it; after I sent you mail last night I went ahead
+and finished up what I thought needed to be done.  I would like to get
+some feedback from you on a few items, if you have time.
+
+There are two programs, mailprio_mkdb and mailprio (source below).
+
+mailprio_mkdb reads maillog files and creates a DB file of address vs.
+delay.  I'm not too happy with how it does the averages right now but this
+is just a quick hack.  However, it should at least order sites that take
+days vs. those that deliver on the first pass through.  One thing that
+would make this information a lot more accurate is if sendmail could log
+a "transaction delay" (on failures also), as well as total delivery delay.
+Perhaps, as an option, it could maintain the DB file itself?
+
+mailprio then simply reads a list of addresses from stdin (the mailing
+list), and tries to prioritize them according to the info the database.
+It collects comment lines and other junk at the top of the file; all
+mailprio does is reorder lines, the actual text of the file should
+be unchanged to the extent that you can verify it with:
+    sort sorted_list > checkit; sort mailing-list | diff - checkit
+Users with no delay information are put next.  The prioritized list is last.
+Of course, this function could also be built-into sendmail (eventually).
+
+Putting "new account" info at the top with the current averaging function
+probably adversly affects the prioritized list (at least in the short
+term), but putting it at the bottom would not really give the new accounts
+a fair chance.  I suspect this isn't that big of a problem.  I'm running
+this here on a list with 461 accounts and about 10 messages per day so
+I'll see how it goes.  I'll keep some stats on delay times and see what
+happens.
+
+Another thing that would help this situation, is if sendmail had the queue
+ordered by site (but you already know this).  If you ever get to do per
+site queuing you should consider "blocking" a queue for some short period
+of time if a connection fails to that site [sendmail does this inside a
+single process on a per account basis now right?]; this would allow multiple
+sendmails to quickly skip over those sites for people like me that run:
+
+    for i in 1 2 3 4 5 6 7 8 ; do daemon sendmail -q; done
+
+to flush a queue that has gotten behind.  You could also do this inside
+sendmail with a parallelism option (when it is time to run the queue, how
+many processes to start).
+
+#! /bin/sh
+# This is a shell archive.  Remove anything before this line, then unpack
+# it by saving it into a file and typing "sh file".  To overwrite existing
+# files, type "sh file -c".  You can also feed this as standard input via
+# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
+# will see the following message at the end:
+#              "End of shell archive."
+# Contents:  mailprio mailprio_mkdb
+# Wrapped by sanders@austin.BSDI.COM on Fri Dec  9 18:07:02 1994
+PATH=/bin:/usr/bin:/usr/ucb ; export PATH
+if test -f 'mailprio' -a "${1}" != "-c" ; then 
+  echo shar: Will not clobber existing file \"'mailprio'\"
+else
+echo shar: Extracting \"'mailprio'\" \(3093 characters\)
+sed "s/^X//" >'mailprio' <<'END_OF_FILE'
+X#!/usr/bin/perl
+X#
+X# mailprio -- setup mail priorities for a mailing list
+X#
+X# Sort mailing list by mailprio database:
+X#     mailprio < mailing-list > sorted_list
+X# Double check against orig:
+X#     sort sorted_list > checkit; sort mailing-list | diff - checkit
+X# If it checks out, install it.
+X#
+X# TODO:
+X#     option to process mqueue files so we can reorder files in the queue!
+X$usage = "Usage: mailprio [-p priodb]\n";
+X$home = "/home/sanders/lists";
+X$priodb = "$home/mailprio";
+X
+Xif ($main'ARGV[0] =~ /^-/) {
+X      $args = shift;
+X      if ($args =~ m/\?/) { print $usage; exit 0; }
+X      if ($args =~ m/p/) {
+X          $priodb = shift || die $usage, "-p requires argument\n"; }
+X}
+X
+X# In shell script, it goes something like this:
+X#     old_mailprio > /tmp/a
+X#     fgrep -f lists/inet-access /tmp/a | sed -e 's/^.......//' > /tmp/b
+X#         ; /tmp/b contains list of known users, faster delivery first
+X#     fgrep -v -f /tmp/b lists/inet-access > /tmp/c
+X#         ; put all unknown stuff at the top of new list for now
+X#     echo '# -----' >> /tmp/c
+X#     cat /tmp/b >> /tmp/c
+X
+X# Setup %list and @list
+Xlocal($addr, $canon);
+Xwhile ($addr = <STDIN>) {
+X    chop $addr;
+X    next if $addr =~ /^# ----- /;                     # that's our line
+X    push(@list, $addr), next if $addr =~ /^\s*#/;     # save comments
+X    $canon = &canonicalize((&simplify_address($addr))[0]);
+X    unless (defined $canon) {
+X      warn "no address found: $addr\n";
+X      push(@list, $addr);                             # save it anyway
+X      next;
+X    }
+X    if (defined $list{$canon}) {
+X      warn "duplicate: ``$addr -> $canon''\n";
+X      push(@list, $addr);                             # save it anyway
+X      next;
+X    }
+X    $list{$canon} = $addr;
+X}
+X
+Xlocal(*prio);
+Xdbmopen(%prio, $priodb, 0644) || die "$priodb: $!\n";
+Xforeach $to (keys %list) {
+X    if (defined $prio{$to}) {
+X      # add to list of found users (%userprio) and remove from %list
+X      # so that we know what users were not yet prioritized
+X      $userprio{$to} = $prio{$to};    # priority
+X      $useracct{$to} = $list{$to};    # string
+X      delete $list{$to};
+X    }
+X}
+Xdbmclose(%prio);
+X
+X# Put all the junk we found at the very top
+X# (this might not always be a feature)
+Xprint join("\n", @list), "\n";
+X
+X# unprioritized users go next, slow accounts will get moved down quickly
+Xprint '# ----- unprioritized users', "\n";
+Xforeach $to (keys %list) { print $list{$to}, "\n"; }
+X
+X# finally, our prioritized list of users
+Xprint '# ----- prioritized users', "\n";
+Xforeach $to (sort { $userprio{$a} <=> $userprio{$b}; } keys %userprio) {
+X    die "Opps! Something is seriously wrong with useracct: $to\n"
+X      unless defined $useracct{$to};
+X    print $useracct{$to}, "\n";
+X}
+X
+Xexit(0);
+X
+X# REPL-LIB ---------------------------------------------------------------
+X
+Xsub canonicalize {
+X    local($addr) = @_;
+X    # lowercase, strip leading/trailing whitespace
+X    $addr =~ y/A-Z/a-z/; $addr =~ s/^\s+//; $addr =~ s/\s+$//; $addr;
+X}
+X
+X# @addrs = simplify_address($addr);
+Xsub simplify_address {
+X    local($_) = shift;
+X    1 while s/\([^\(\)]*\)//g;                # strip comments
+X    1 while s/"[^"]*"//g;             # strip comments
+X    split(/,/);                               # split into parts
+X    foreach (@_) {
+X      1 while s/.*<(.*)>.*/\1/;
+X      s/^\s+//;
+X      s/\s+$//;
+X    }
+X    @_;
+X}
+END_OF_FILE
+if test 3093 -ne `wc -c <'mailprio'`; then
+    echo shar: \"'mailprio'\" unpacked with wrong size!
+fi
+chmod +x 'mailprio'
+# end of 'mailprio'
+fi
+if test -f 'mailprio_mkdb' -a "${1}" != "-c" ; then 
+  echo shar: Will not clobber existing file \"'mailprio_mkdb'\"
+else
+echo shar: Extracting \"'mailprio_mkdb'\" \(3504 characters\)
+sed "s/^X//" >'mailprio_mkdb' <<'END_OF_FILE'
+X#!/usr/bin/perl
+X#
+X# mailprio_mkdb -- make mail priority database based on delay times
+X#
+X$usage = "Usage: mailprio_mkdb [-l maillog] [-p priodb]\n";
+X$home = "/home/sanders/lists";
+X$maillog = "/var/log/maillog";
+X$priodb = "$home/mailprio";
+X
+Xif ($main'ARGV[0] =~ /^-/) {
+X      $args = shift;
+X      if ($args =~ m/\?/) { print $usage; exit 0; }
+X      if ($args =~ m/l/) {
+X          $maillog = shift || die $usage, "-l requires argument\n"; }
+X      if ($args =~ m/p/) {
+X          $priodb = shift || die $usage, "-p requires argument\n"; }
+X}
+X
+Xlocal(*prio);
+X# We'll merge with existing information if it's already there.
+Xdbmopen(%prio, $priodb, 0644) || die "$priodb: $!\n";
+X&getlog_stats($maillog, *prio);
+X# foreach $addr (sort { $prio{$a} <=> $prio{$b}; } keys %prio) {
+X#     printf("%06d %s\n", $prio{$addr}, $addr); }
+Xdbmclose(%prio);
+Xexit(0);
+X
+Xsub getlog_stats {
+X    local($maillog, *stats) = @_;
+X    local($to, $delay);
+X    local($h, $m, $s);
+X    open(MAILLOG, "< $maillog") || die "$maillog: $!\n";
+X    while (<MAILLOG>) {
+X      ($delay) = (m/, delay=([^,]*), /);
+X      $delay || next;
+X      ($h, $m, $s) = split(/:/, $delay);
+X      $delay = ($h * 60 * 60) + ($m * 60) + $s;
+X
+X      # deleting everything after ", " seems safe enough, though
+X      # it is possible that it was inside "..."'s and that we will
+X      # miss some addresses because of it.  However, I'm not willing
+X      # to do full parsing just for that case.  If this bothers you
+X      # you could do something like: s/, (delay|ctladdr)=.*//;
+X      # but you have to make sure you catch all the possible names.
+X      $to = $_; $to =~ s/^.* to=//; $to =~ s/, .*//;
+X      foreach $addr (&simplify_address($to)) {
+X          next unless $addr;
+X          $addr = &canonicalize($addr);
+X          # print $delay, " ", $addr, "\n";
+X          $stats{$addr} = $delay unless defined $stats{$addr};        # init
+X
+X          # This average function moves the value around quite rapidly
+X          # which may or may not be a feature.
+X          #
+X          # This has at least one odd behavior because we currently only
+X          # use the delay information from maillog which is only logged
+X          # on actual delivery.  This works backwards from what we really
+X          # want to happen when a fast host goes down for a while and then
+X          # comes back up.
+X          #
+X          # I spoke with Eric and he suggested adding an xdelay statistic
+X          # for a per transaction delay which would help that situation
+X          # a lot.  What I believe you want in that cases something like:
+X          #   delay fast, xdelay fast: smokin', these hosts go first
+X          #   delay slow, xdelay fast: put host high on the list (back up?)
+X          #   delay fast, xdelay slow: host is down/having problems/slow
+X          #   delay slow, xdelay slow: poorly connected sites, very last
+X          # Of course, you have to reorder the distribution list fairly
+X          # often for that to help.  Come to think of it, you should
+X          # also reorder /var/spool/mqueue files also (if they aren't
+X          # locked of course).  Hmmm....
+X          $stats{$addr} = int(($stats{$addr} + $delay) / 2);
+X      }
+X    }
+X    close(MAILLOG);
+X}
+X
+X# REPL-LIB ---------------------------------------------------------------
+X
+Xsub canonicalize {
+X    local($addr) = @_;
+X    # lowercase, strip leading/trailing whitespace
+X    $addr =~ y/A-Z/a-z/; $addr =~ s/^\s+//; $addr =~ s/\s+$//; $addr;
+X}
+X
+X# @addrs = simplify_address($addr);
+Xsub simplify_address {
+X    local($_) = shift;
+X    1 while s/\([^\(\)]*\)//g;                # strip comments
+X    1 while s/"[^"]*"//g;             # strip comments
+X    split(/,/);                               # split into parts
+X    foreach (@_) {
+X      1 while s/.*<(.*)>.*/\1/;
+X      s/^\s+//;
+X      s/\s+$//;
+X    }
+X    @_;
+X}
+END_OF_FILE
+if test 3504 -ne `wc -c <'mailprio_mkdb'`; then
+    echo shar: \"'mailprio_mkdb'\" unpacked with wrong size!
+fi
+chmod +x 'mailprio_mkdb'
+# end of 'mailprio_mkdb'
+fi
+echo shar: End of shell archive.
+exit 0
diff --git a/usr.sbin/sendmail/contrib/rmail.oldsys.patch b/usr.sbin/sendmail/contrib/rmail.oldsys.patch
new file mode 100644 (file)
index 0000000..856fcf1
--- /dev/null
@@ -0,0 +1,108 @@
+From: Bill Gianopoulos <wag@sccux1.msd.ray.com>
+Message-Id: <199405191527.LAA03463@sccux1.msd.ray.com>
+Subject: Patch to rmail to elliminate need for snprintf
+To: sendmail@CS.Berkeley.EDU
+Date: Thu, 19 May 1994 11:27:16 -0400 (EDT)
+
+I have written the following patch to rmail which removes the requirement
+for snprintf while maintaining the protection from buffer overruns.  It also
+fixes it to compile with compilers which don't understand ANSI function
+prototypes.  Perhaps this should be included in the next version?
+
+*** rmail/rmail.c.orig Mon May 31 18:10:44 1993
+--- rmail/rmail.c      Thu May 19 11:04:50 1994
+***************
+*** 78,86 ****
+--- 78,109 ----
+  #include <sysexits.h>
+  #include <unistd.h>
+  
++ #ifdef __STDC__
+  void err __P((int, const char *, ...));
+  void usage __P((void));
++ #else
++ void err ();
++ void usage ();
++ #endif
+  
++ #define strdup(s)    strcpy(xalloc(strlen(s) + 1), s)
++ 
++ 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)
++              err(EX_UNAVAILABLE, "Out of memory!!");
++      return (p);
++ }
++ 
+  int
+  main(argc, argv)
+       int argc;
+***************
+*** 230,250 ****
+       args[i++] = "-oi";              /* Ignore '.' on a line by itself. */
+  
+       if (from_sys != NULL) {         /* Set sender's host name. */
+!              if (strchr(from_sys, '.') == NULL)
+!                      (void)snprintf(buf, sizeof(buf),
+                           "-oMs%s.%s", from_sys, domain);
+!              else
+!                      (void)snprintf(buf, sizeof(buf), "-oMs%s", from_sys);
+               if ((args[i++] = strdup(buf)) == NULL)
+                        err(EX_TEMPFAIL, NULL);
+       }
+                                       /* Set protocol used. */
+!      (void)snprintf(buf, sizeof(buf), "-oMr%s", domain);
+       if ((args[i++] = strdup(buf)) == NULL)
+               err(EX_TEMPFAIL, NULL);
+  
+                                       /* Set name of ``from'' person. */
+!      (void)snprintf(buf, sizeof(buf), "-f%s%s",
+           from_path ? from_path : "", from_user);
+       if ((args[i++] = strdup(buf)) == NULL)
+               err(EX_TEMPFAIL, NULL);
+--- 253,285 ----
+       args[i++] = "-oi";              /* Ignore '.' on a line by itself. */
+  
+       if (from_sys != NULL) {         /* Set sender's host name. */
+!              if (strchr(from_sys, '.') == NULL) {
+!                      if ((strlen(from_sys) + strlen(domain) + 6)
+!                          > sizeof(buf))
+!                              err(EX_DATAERR, "sender hostname too long");
+!                      (void)sprintf(buf,
+                           "-oMs%s.%s", from_sys, domain);
+!              }
+!              else {
+!                      if ((strlen(from_sys) + 5) > sizeof(buf))
+!                              err(EX_DATAERR ,"sender hostname too long");
+!                      (void)sprintf(buf, "-oMs%s", from_sys);
+!              }
+               if ((args[i++] = strdup(buf)) == NULL)
+                        err(EX_TEMPFAIL, NULL);
+       }
+                                       /* Set protocol used. */
+!      if ((strlen(domain) + 5) > sizeof(buf))
+!              err(EX_DATAERR, "protocol name too long");
+!      (void)sprintf(buf, "-oMr%s", domain);
+       if ((args[i++] = strdup(buf)) == NULL)
+               err(EX_TEMPFAIL, NULL);
+  
+                                       /* Set name of ``from'' person. */
+!      if (((from_path ? strlen(from_path) : 0) + strlen(from_user) + 3) 
+!          > sizeof(buf))
+!              err(EX_DATAERR, "from address too long");
+!      (void)sprintf(buf, "-f%s%s",
+           from_path ? from_path : "", from_user);
+       if ((args[i++] = strdup(buf)) == NULL)
+               err(EX_TEMPFAIL, NULL);
+-- 
+William A. Gianopoulos; Raytheon Missile Systems Division
+wag@sccux1.msd.ray.com
index 0b91ed0..ee838bd 100644 (file)
@@ -30,7 +30,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\"    @(#)changes.me  8.1 (Berkeley) 4/13/94
+.\"    @(#)changes.me  8.2 (Berkeley) 5/3/95
 .\"
 .\"    ditroff -me -Pxx changes.me
 .eh '%''Changes in Sendmail Version 8'
@@ -454,15 +454,15 @@ Data General DG/UX,
 HP-UX,
 Sequent Dynix,
 or
-Ultrix.
+Ultrix 4.x, x \(<= 3.
 It seems to work on
 4.4BSD,
 IBM AIX 3.x,
 OSF/1,
 SGI IRIX,
 Solaris,
-and
-SunOS.
+SunOS,
+and Ultrix 4.4.
 .sh 2 "Separate Envelope/Header Processing
 .pp
 Since the From: line is passed in separately from the envelope
index 9678d14..6fd40ad 100644 (file)
@@ -1,4 +1,4 @@
-.\" Copyright (c) 1983 Eric P. Allman
+.\" Copyright (c) 1983, 1995 Eric P. Allman
 .\" Copyright (c) 1983, 1993
 .\"    The Regents of the University of California.  All rights reserved.
 .\"
@@ -30,7 +30,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\"    @(#)op.me       8.36 (Berkeley) 4/14/94
+.\"    @(#)op.me       8.70 (Berkeley) 11/3/95
 .\"
 .\" eqn op.me | pic | troff -me
 .eh 'SMM:08-%''Sendmail Installation and Operation Guide'
 .sp
 .r
 Eric Allman
-University of California, Berkeley
-Mammoth Project
-eric@CS.Berkeley.EDU
+Pang\(aea Reference Systems
+eric@Sendmail.ORG
 .sp
-Version 8.36
+Version 8.70
 .sp
-For Sendmail Version 8.6
+For Sendmail Version 8.7
 .)l
 .sp 2
 .pp
 .i Sendmail
 implements a general purpose internetwork mail routing facility
-under the UNIX*
-.(f
-*UNIX is a trademark of Unix Systems Laboratories.
-.)f
+under the UNIX\(rg
 operating system.
 It is not tied to any one transport protocol \*-
 its function may be likened to a crossbar switch,
@@ -104,11 +100,22 @@ incrementally.
 .pp
 .i Sendmail
 is based on
-RFC822 (Internet Mail Format Protocol),
 RFC821 (Simple Mail Transport Protocol),
+RFC822 (Internet Mail Format Protocol),
 RFC1123 (Internet Host Requirements),
+RFC1521 (MIME),
+RFC1651 (SMTP Service Extensions),
+and a series of as-yet-draft standards describing
+Delivery Status Notifications (DSNs),
+available from the internet drafts sites as
+draft-ietf-notary-mime-delivery-\fIXX\fP.txt,
+draft-ietf-notary-mime-report-\fIXX\fP.txt,
+draft-ietf-notary-smtp-drpt-\fIXX\fP.txt,
 and
-RFC1425 (SMTP Service Extensions).
+draft-ietf-notary-status-\fIXX\fP.txt
+(replace
+.i XX
+by the latest draft number).
 However, since
 .i sendmail
 is designed to work in a wider world,
@@ -153,6 +160,12 @@ in this version of
 The appendixes give a brief
 but detailed explanation of a number of features
 not described in the rest of the paper.
+.pp
+.b WARNING:
+Several major changes were introduced in version 8.7.
+You should not attempt to use this document
+for prior versions of
+.i sendmail .
 .bp 7
 .sh 1 "BASIC INSTALLATION"
 .pp
@@ -199,29 +212,23 @@ If you are running on a 4.4BSD system,
 compile by typing
 .q make .
 On other systems, you may have to make some other adjustments.
-.sh 3 "Old versions of make"
-.pp
-If you are not running the new version of
-.b make
-you will probably have to use
+On most systems,
+you can do the appropriate compilation by typing
 .(b
-make \-f Makefile.dist
+sh makesendmail
 .)b
-This file does not assume several new syntaxes,
-including the
-.q +=
-syntax in macro definition
-and the
-.q ".include"
-syntax.
-.sh 3 "Compilation flags"
+This will leave the binary in an appropriately named subdirectory.
+It works for multiple object versions
+compiled out of the same directory.
+.sh 3 "Tweaking the Makefile"
 .pp
 .i Sendmail
 supports two different formats
-for the
+for the local (on disk) version of databases,
+notably the
 .i aliases
 database.
-These formats are:
+At least one of these should be defined if at all possible.
 .nr ii 1i
 .ip NDBM
 The ``new DBM'' format,
@@ -243,9 +250,8 @@ old databases are read,
 but when a new database is created it will be in NEWDB format.
 As a nasty hack,
 if you have NEWDB, NDBM, and NIS defined,
-and if the file
-.i /var/yp/Makefile
-exists and is readable,
+and if the alias file name includes the substring
+.q /yp/ ,
 .i sendmail
 will create both new and old versions of the alias file
 during a
@@ -260,47 +266,36 @@ If neither of these are defined,
 .i sendmail
 reads the alias file into memory on every invocation.
 This can be slow and should be avoided.
-.pp
-System V based systems can define
-SYSTEM5
-to make several small adjustments.
-This changes the handling of timezones
-and uses the much less efficient
-.i lockf
-call in preference to
-.i flock .
-These can be specified separately using the compilation flags
-SYS5TZ
-and
-LOCKF
-respectively.
-.pp
-If you don't have the
-.i unsetenv
-routine in your system library, define the UNSETENV compilation flag.
-.pp
-You may also have to define the compilation variable LA_TYPE
-to describe how your load average is computed.
-This and other flags are detailed in section 6.1.
+There are also several methods for remote database access:
+.ip NIS
+Sun's Network Information Services (formerly YP).
+.ip NISPLUS
+Sun's NIS+ services.
+.ip NETINFO
+NeXT's NetInfo service.
+.ip HESIOD
+Hesiod service (from Athena).
+.lp
+Other compilation flags are set in conf.h
+and should be predefined for you
+unless you are porting to a new environment.
 .sh 3 "Compilation and installation"
 .pp
 After making the local system configuration described above,
 You should be able to compile and install the system.
-Compilation can be performed using
-.q make\**
-.(f
-\**where you may have to replace
-.q make
-with
-.q "make \-f Makefile.dist"
-as appropriate.
-.)f
-in the
-.b sendmail/src
-directory.
+The script
+.q makesendmail
+is the best approach on most systems:
+.(b
+sh makesendmail
+.)b
+This will use
+.i uname (1)
+to select the correct Makefile for your environment.
+.pp
 You may be able to install using
 .(b
-make install
+sh makesendmail install
 .)b
 This should install the binary in
 /usr/\*(SD
@@ -315,7 +310,7 @@ On 4.4BSD systems it will also format and install man pages.
 .pp
 .i Sendmail
 cannot operate without a configuration file.
-The configuration defines the mail systems understood at this site,
+The configuration defines the mail delivery mechanisms understood at this site,
 how to access them,
 how to forward email to remote mail systems,
 and a number of tuning parameters.
@@ -348,12 +343,6 @@ they will use
 The configuration files can be customized to work around this,
 but it is more complex.
 .pp
-I haven't tested these yet on an isolated LAN environment
-with a single UUCP connection to the outside world.
-If you are in such an environment,
-please send comments to
-sendmail@CS.Berkeley.EDU.
-.pp
 Our configuration files are processed by
 .i m4
 to facilitate local customization;
@@ -435,12 +424,15 @@ referenced using the
 .sm MAILER
 .b m4
 macro.
-Defined mailer types in this distribution are
+The mailer types that are known in this distribution are
 fax,
 local,
 smtp,
 uucp,
 and usenet.
+For example, to include support for the UUCP-based mailers,
+use
+.q MAILER(uucp) .
 .ip ostype
 Definitions describing various operating system environments
 (such as the location of support files).
@@ -549,8 +541,22 @@ and permissions are
 .sh 3 "/etc/sendmail.cf"
 .pp
 This is the configuration file for
-.i sendmail .
-This is the only non-library file name compiled into
+.i sendmail \**.
+.(f
+\**Actually, the pathname varies depending on the operating system;
+/etc is the preferred directory.
+Some older systems install it in
+.b /usr/lib/sendmail.cf ,
+and I've also seen it in
+.b /usr/ucblib
+and
+.b /etc/mail .
+If you want to move this file,
+change
+.i src/conf.h .
+.)f
+This and /etc/sendmail.pid
+are the only non-library file names compiled into
 .i sendmail \**.
 .(f
 \**The system libraries can reference other files;
@@ -561,12 +567,6 @@ calls probably reference
 and
 .i /etc/resolv.conf .
 .)f
-Some older systems install it in
-.b /usr/lib/sendmail.cf .
-.pp
-If you want to move this file,
-change
-.i src/pathnames.h .
 .pp
 The configuration file is normally created
 using the distribution files described above.
@@ -690,8 +690,10 @@ causes it to run the queue every half hour.
 .pp
 Some people use a more complex startup script,
 removing zero length qf files and df files for which there is no qf file.
-For example:
-.(b
+For example, see Figure 1
+for an example of a complex startup script.
+.(z
+.hl
 # remove zero length qf files
 for qffile in qf*
 do
@@ -732,7 +734,11 @@ for xffile in [A-Z]f*
 do
        echo \-n " <panic: $xffile>" > /dev/console
 done
-.)b
+.sp
+.ce
+Figure 1 \(em A complex startup script
+.hl
+.)z
 .pp
 If you are not running a version of UNIX
 that supports Berkeley TCP/IP,
@@ -774,18 +780,6 @@ is defined in the
 option of the
 .i sendmail.cf
 file.
-.sh 3 "/usr/\*(SB/newaliases"
-.pp
-If
-.i sendmail
-is invoked as
-.q newaliases,
-it will simulate the
-.b \-bi
-flag
-(i.e., will rebuild the alias database;
-see below).
-This should be a link to /usr/\*(SD/sendmail.
 .sh 3 "/usr/\*(SB/mailq"
 .pp
 If
@@ -810,7 +804,11 @@ All messages from
 .i sendmail
 are logged under the
 .sm LOG_MAIL
-facility.
+facility\**.
+.(f
+\**Except on Ultrix,
+which does not support facilities in the syslog.
+.)f
 .sh 3 "Format"
 .pp
 Each line in the system log
@@ -820,7 +818,64 @@ the name of the machine that generated it
 over the local area network),
 the word
 .q sendmail: ,
-and a message.
+and a message\**.
+.(f
+\**This format may vary slightly if your vendor has changed
+the syntax.
+.)f
+Most messages are a sequence of
+.i name \c
+=\c
+.i value
+pairs.
+.pp
+The two most common lines are logged when a message is processed.
+The first logs the receipt of a message;
+there will be exactly one of these per message.
+Some fields may be omitted if they do not contain interesting information.
+Fields are:
+.ip from
+The envelope sender address.
+.ip size
+The size of the message in bytes.
+.ip class
+The class (i.e., numeric precedence) of the message.
+.ip pri
+The initial message priority (used for queue sorting).
+.ip nrcpts
+The number of envelope recipients for this message
+(after aliasing and forwarding).
+.ip msgid
+The message id of the message (from the header).
+.ip proto
+The protocol used to receive this message (e.g., ESMTP or UUCP)
+.ip relay
+The machine from which it was received.
+.lp
+There is also one line logged per delivery attempt
+(so there can be several per message if delivery is deferred
+or there are multiple recipients).
+Fields are:
+.ip to
+A comma-separated list of the recipients to this mailer.
+.ip ctladdr
+The ``controlling user'', that is, the name of the user
+whose credentials we use for delivery.
+.ip delay
+The total delay between the time this message was received
+and the time it was delivered.
+.ip xdelay
+The amount of time needed in this delivery attempt
+(normally indicative of the speed of the connection).
+.ip mailer
+The name of the mailer used to deliver to this recipient.
+.ip relay
+The name of the host that actually accepted (or rejected) this recipient.
+.ip stat
+The delivery status.
+.lp
+Not all fields are present in all messages;
+for example, the relay is not listed for local deliveries.
 .sh 3 "Levels"
 .pp
 If you have
@@ -844,10 +899,30 @@ Levels from 11\-64 are reserved for verbose information
 that some sites might want.
 .pp
 A complete description of the log levels
-is given in section 4.6.
+is given in section
+.\" XREF
+4.6.
+.sh 2 "Dumping State"
+.pp
+You can ask
+.i sendmail
+to log a dump of the open files
+and the connection cache
+by sending it a
+.sm SIGUSR1
+signal.
+The results are logged at
+.sm LOG_DEBUG
+priority.
 .sh 2 "The Mail Queue"
 .pp
-The mail queue should be processed transparently.
+Sometimes a host cannot handle a message immediately.
+For example, it may be down or overloaded, causing it to refuse connections.
+The sending host is then expected to save this message in
+its mail queue
+and attempt to deliver it later.
+.pp
+Under normal conditions the mail queue will be processed transparently.
 However, you may find that manual intervention is sometimes necessary.
 For example,
 if a major host is down for a period of time
@@ -947,6 +1022,56 @@ you can remove the directory:
 .(b
 rmdir /var/spool/omqueue
 .)b
+.sh 2 "The Service Switch"
+.pp
+The implementation of certain system services
+such as host and user name lookup
+is controlled by the service switch.
+If the host operating system supports such a switch
+.i sendmail
+will use the native version.
+Ultrix, Solaris, and DEC OSF/1 are examples of such systems.
+.pp
+If the underlying operating system does not support a service switch
+(e.g., SunOS, HP-UX, BSD)
+then
+.i sendmail
+will provide a stub implementation.
+The
+.b ServiceSwitchFile
+option points to the name of a file that has the service definitions
+Each line has the name of a service
+and the possible implementations of that service.
+For example, the file:
+.(b
+hosts  dns files nis
+aliases        files nis
+.)b
+will ask
+.i sendmail
+to look for hosts in the Domain Name System first.
+If the requested host name is not found,
+it tries local files,
+and if that fails it tries NIS.
+Similarly,
+when looking for aliases
+it will try the local files first
+followed by NIS.
+.pp
+Service switches are not completely integrated.
+For example, despite the fact that the host entry listed in the above example
+specifies to look in NIS,
+on SunOS this won't happen because the system implementation of
+.i gethostbyname \|(3)
+doesn't understand this.
+If there is enough demand
+.i sendmail
+may reimplement
+.i gethostbyname \|(3),
+.i gethostbyaddr \|(3),
+.i getpwent \|(3),
+and the other system routines that would be necessary
+to make this work seamlessly.
 .sh 2 "The Alias Database"
 .pp
 The alias database exists in two forms.
@@ -962,7 +1087,14 @@ e.g.,
 .(b
 eric@prep.ai.MIT.EDU: eric@CS.Berkeley.EDU
 .)b
-will not have the desired effect.
+will not have the desired effect
+(except on prep.ai.MIT.EDU,
+and they probably don't want me)\**.
+.(f
+\**Actually, any mailer that has the `A' mailer flag set
+will permit aliasing;
+this is normally limited to the local mailer.
+.)f
 Aliases may be continued by starting any continuation lines
 with a space or a tab.
 Blank lines and lines beginning with a sharp sign
@@ -971,9 +1103,14 @@ Blank lines and lines beginning with a sharp sign
 are comments.
 .pp
 The second form is processed by the
-.i dbm \|(3)
-(or
-.i db \|(3))
+.i ndbm \|(3)\**
+.(f
+\**The
+.i gdbm
+package probably works as well.
+.)f
+or
+.i db \|(3)
 library.
 This form is in the files
 .i /etc/aliases.dir
@@ -984,6 +1121,30 @@ This is the form that
 actually uses to resolve aliases.
 This technique is used to improve performance.
 .pp
+The control of search order is actually set by the service switch.
+Essentially, the entry
+.(b
+OAswitch:aliases
+.)b
+is always added as the first alias entry;
+also, the first alias file name without a class
+(e.g., without
+.q nis:
+on the front)
+will be used as the name of the file for a ``files'' entry
+in the aliases switch.
+For example, if the configuration file contains
+.(b
+OA/etc/aliases
+.)b
+and the service switch contains
+.(b
+aliases        nis files nisplus
+.)b
+then aliases will first be searched in the NIS database,
+then in /etc/aliases,
+then in the NIS+ database.
+.pp
 You can also use
 .sm NIS -based
 alias files.
@@ -1013,7 +1174,7 @@ exactly like a
 .b K
 line \(em for example:
 .(b
-OAnis:-N mail.aliases@my.nis.domain
+OAnis:\-N mail.aliases@my.nis.domain
 .)b
 will search the appropriate NIS map and always include null bytes in the key.
 .sh 3 "Rebuilding the alias database"
@@ -1033,7 +1194,9 @@ flag:
 .)b
 .pp
 If the
-.q D
+.b RebuildAliases
+(old
+.b D )
 option is specified in the configuration,
 .i sendmail
 will rebuild the alias database automatically
@@ -1042,7 +1205,12 @@ when it is out of date.
 Auto-rebuild can be dangerous
 on heavily loaded machines
 with large alias files;
-if it might take more than five minutes
+if it might take more than the rebuild timeout
+(option
+.b AliasWait ,
+old
+.b a ,
+which is normally five minutes)
 to rebuild the database,
 there is a chance that several processes will start the rebuild process
 simultaneously.
@@ -1051,7 +1219,7 @@ If you have multiple aliases databases specified,
 the
 .b \-bi
 flag rebuilds all the database types it understands
-(for example, it can rebuild dbm databases but not nis databases).
+(for example, it can rebuild NDBM databases but not NIS databases).
 .sh 3 "Potential problems"
 .pp
 There are a number of problems that can occur
@@ -1067,11 +1235,14 @@ or the process rebuilding the database dies
 (due to being killed or a system crash)
 before completing the rebuild.
 .pp
-Sendmail has two techniques to try to relieve these problems.
+Sendmail has three techniques to try to relieve these problems.
 First, it ignores interrupts while rebuilding the database;
 this avoids the problem of someone aborting the process
 leaving a partially rebuilt database.
 Second,
+it locks the database source file during the rebuild \(em
+but that may not work over NFS or if the file is unwritable.
+Third,
 at the end of the rebuild
 it adds an alias of the form
 .(b
@@ -1084,7 +1255,7 @@ will access the database,
 it checks to insure that this entry exists\**.
 .(f
 \**The
-.q a
+.b AliasWait
 option is required in the configuration
 for this action to occur.
 This should normally be specified.
@@ -1108,7 +1279,8 @@ For example:
 .(b
 unix-wizards: eric@ucbarpa, wnj@monet, nosuchuser,
        sam@matisse
-owner-unix-wizards: eric@ucbarpa
+owner-unix-wizards: unix-wizards-request
+unix-wizards-request: eric@ucbarpa
 .)b
 would cause
 .q eric@ucbarpa
@@ -1123,12 +1295,14 @@ List owners also cause the envelope sender address to be modified.
 The contents of the owner alias are used if they point to a single user,
 otherwise the name of the alias itself is used.
 For this reason, and to obey Internet conventions,
-a typical scheme would be:
-.(b
-list:  some, set, of, addresses
-list-request:  list-admin-1, list-admin-2, ...
-owner-list:    list-request
-.)b
+the
+.q owner-
+address normally points at the
+.q -request
+address; this causes messages to go out with the typical Internet convention
+of using ``\c
+.i list -request''
+as the return address.
 .sh 2 "User Information Database"
 .pp
 If you have a version of
@@ -1142,12 +1316,6 @@ the databases will be searched for a
 .i user :maildrop
 entry.
 If found, the mail will be sent to the specified address.
-.pp
-If the first token passed to user part of the
-.q local
-mailer is an at sign,
-the at sign will be stripped off
-and this step will be skipped.
 .sh 2 "Per-User Forwarding (.forward Files)"
 .pp
 As an alternative to the alias database,
@@ -1185,30 +1353,6 @@ Others have interpretations built into
 .i sendmail
 that cannot be changed without changing the code.
 These builtins are described here.
-.sh 3 "Return-Receipt-To:"
-.pp
-If this header is sent,
-a message will be sent to any specified addresses
-when the final delivery is complete,
-that is,
-when successfully delivered to a mailer with the
-.b l
-flag (local delivery) set in the mailer descriptor\**.
-.(f
-\**Some sites disable this header,
-and other (non-\c
-.i sendmail )
-systems do not implement it.
-Do not assume that a failure to get a return receipt
-means that the mail did not arrive.
-Also, do not assume that getting a return receipt
-means that the mail has been read;
-it just means that the message has been delivered
-to the recipient's mailbox.
-.)f
-This header can be disabled with the
-.q noreceipts
-privacy flag.
 .sh 3 "Errors-To:"
 .pp
 If errors occur anywhere during processing,
@@ -1222,21 +1366,35 @@ this was a hack to provide what should now be passed
 as the envelope sender address.
 It should go away.
 It is only used if the
-.b l
+.b UseErrorsTo
 option is set.
+.pp
+The Errors-To: header is official deprecated
+and will go away in a future release.
 .sh 3 "Apparently-To:"
 .pp
+RFC 822 requires at least one recipient field
+(To:, Cc:, or Bcc: line)
+in every message.
 If a message comes in with no recipients listed in the message
-(in a To:, Cc:, or Bcc: line)
 then
 .i sendmail
-will add an
+will adjust the header based on the
+.q NoRecipientAction
+option.
+One of the possible actions is to add an
 .q "Apparently-To:"
 header line for any recipients it is aware of.
 This is not put in as a standard recipient line
 to warn any recipients that the list is not complete.
 .pp
-At least one recipient line is required under RFC 822.
+The Apparently-To: header is non-standard
+and is deprecated.
+.sh 3 "Precedence"
+.pp
+The Precedence: header can be used as a crude control of message priority.
+It tweaks the sort order in the queue
+and can be configured to change the message timeout values.
 .sh 2 "IDENT Protocol Support"
 .pp
 .i Sendmail
@@ -1281,6 +1439,15 @@ the CallerID service apply to Identification.  If you wouldn't run a
 "finger" server due to privacy considerations you may not want to run
 this protocol.
 .ba
+.lp
+In some cases your system may not work properly with IDENT support
+due to a bug in the TCP/IP implementation.
+The symptoms will be that for some hosts
+the SMTP connection will be closed
+almost immediately.
+If this is true or if you do not want to use IDENT,
+you should set the IDENT timeout to zero;
+this will disable the IDENT protocol.
 .sh 1 "ARGUMENTS"
 .pp
 The complete list of arguments to
@@ -1294,10 +1461,10 @@ to run through the queue
 is defined by the
 .b \-q
 flag.
-If you run in mode
-.b f
+If you run with delivery mode set to
+.b i
 or
-.b a
+.b b
 this can be relatively large,
 since it will only be relevant
 when a host that was down comes back up.
@@ -1307,6 +1474,7 @@ mode
 it should be relatively short,
 since it defines the maximum amount of time that a message
 may sit in the queue.
+(See also the MinQueueAge option.)
 .pp
 RFC 1123 section 5.3.1.1 says that this value should be at least 30 minutes
 (although that probably doesn't make sense if you use ``queue-only'' mode).
@@ -1327,6 +1495,23 @@ flag may be combined in one call:
 .(b
 /usr/\*(SD/sendmail \-bd \-q30m
 .)b
+.pp
+An alternative approach is to invoke sendmail from
+.i inetd (8)
+(use the
+.b \-bs
+flag to ask sendmail to speak SMTP on its standard input and output).
+This works and allows you to wrap
+.i sendmail
+in a TCP wrapper program,
+but may be a bit slower since the configuration file
+has to be re-read on every message that comes in.
+If you do this, you still need to have a
+.i sendmail
+running to flush the queue:
+.(b
+/usr/\*(SD/sendmail \-q30m
+.)b
 .sh 2 "Forcing the Queue"
 .pp
 In some cases you may find that the queue has gotten clogged for some reason.
@@ -1354,7 +1539,7 @@ Similarly,
 .q \-qSstring
 limits the run to particular senders and
 .q \-qIstring
-limits it to particular identifiers.
+limits it to particular queue identifiers.
 .sh 2 "Debugging"
 .pp
 There are a fairly large number of debug flags
@@ -1374,7 +1559,7 @@ the syntax is:
 .(b
 .ta \w'debug-option  'u
 debug-flag:    \fB\-d\fP debug-list
-debug-list:    debug-option [ , debug-option ]
+debug-list:    debug-option [ , debug-option ]*
 debug-option:  debug-range [ . debug-level ]
 debug-range:   integer | integer \- integer
 debug-level:   integer
@@ -1384,12 +1569,41 @@ For example,
 .(b
 \-d12  Set flag 12 to level 1
 \-d12.3        Set flag 12 to level 3
-\-d3-17        Set flags 3 through 17 to level 1
-\-d3-17.4      Set flags 3 through 17 to level 4
+\-d3\-17       Set flags 3 through 17 to level 1
+\-d3\-17.4     Set flags 3 through 17 to level 4
 .)b
 For a complete list of the available debug flags
 you will have to look at the code
 (they are too dynamic to keep this documentation up to date).
+.sh 2 "Changing the Values of Options"
+.pp
+Options can be overridden using the
+.b \-o
+or
+.b \-O
+command line flags.
+For example,
+.(b
+/usr/\*(SD/sendmail \-oT2m
+.)b
+sets the
+.b T
+(timeout) option to two minutes
+for this run only;
+the equivalent line using the long option name is
+.(b
+/usr/\*(SD/sendmail -OQueueTimeout=2m
+.)b
+.pp
+Some options have security implications.
+Sendmail allows you to set these,
+but relinquishes its setuid root permissions thereafter\**.
+.(f
+\**That is, it sets its effective uid to the real uid;
+thus, if you are executing as root,
+as from root's crontab file or during system startup
+the root permissions will still be honored.
+.)f
 .sh 2 "Trying a Different Configuration File"
 .pp
 An alternative configuration file
@@ -1397,7 +1611,7 @@ can be specified using the
 .b \-C
 flag; for example,
 .(b
-/usr/\*(SD/sendmail \-Ctest.cf
+/usr/\*(SD/sendmail \-Ctest.cf \-oQ/tmp/mqueue
 .)b
 uses the configuration file
 .i test.cf
@@ -1409,23 +1623,12 @@ flag has no value
 it defaults to
 .i sendmail.cf
 in the current directory.
-.sh 2 "Changing the Values of Options"
-.pp
-Options can be overridden using the
-.b \-o
-flag.
-For example,
-.(b
-/usr/\*(SD/sendmail \-oT2m
-.)b
-sets the
-.b T
-(timeout) option to two minutes
-for this run only.
 .pp
-Some options have security implications.
-Sendmail allows you to set these,
-but refuses to run as root thereafter.
+.i Sendmail
+gives up its setuid root permissions
+when you use this flag, so it is common to use a publicly writable directory
+(such as /tmp)
+as the spool directory (QueueDirectory or Q option) while testing.
 .sh 2 "Logging Traffic"
 .pp
 Many SMTP implementations do not fully implement the protocol.
@@ -1437,12 +1640,14 @@ If you suspect such a problem, you can set traffic logging using the
 flag.
 For example,
 .(b
-/usr/\*(SD/sendmail \-X /tmp/traffic -bd
+/usr/\*(SD/sendmail \-X /tmp/traffic \-bd
 .)b
 will log all traffic in the file
 .i /tmp/traffic .
 .pp
-This logs a lot of data very quickly and should never be used
+This logs a lot of data very quickly and should
+.b NEVER
+be used
 during normal operations.
 After starting up such a daemon,
 force the errant implementation to send a message to your host.
@@ -1450,18 +1655,99 @@ All message traffic in and out of
 .i sendmail ,
 including the incoming SMTP traffic,
 will be logged in this file.
-.sh 2 "Dumping State"
+.sh 2 "Testing Configuration Files"
 .pp
-You can ask
+When you build a configuration table,
+you can do a certain amount of testing
+using the
+.q "test mode"
+of
+.i sendmail .
+For example,
+you could invoke
 .i sendmail
-to log a dump of the open files
-and the connection cache
-by sending it a
-.sm SIGUSR1
-signal.
-The results are logged at
-.sm LOG_DEBUG
-priority.
+as:
+.(b
+sendmail \-bt \-Ctest.cf
+.)b
+which would read the configuration file
+.q test.cf
+and enter test mode.
+In this mode,
+you enter lines of the form:
+.(b
+rwset address
+.)b
+where
+.i rwset
+is the rewriting set you want to use
+and
+.i address
+is an address to apply the set to.
+Test mode shows you the steps it takes
+as it proceeds,
+finally showing you the address it ends up with.
+You may use a comma separated list of rwsets
+for sequential application of rules to an input.
+For example:
+.(b
+3,1,21,4 monet:bollard
+.)b
+first applies ruleset three to the input
+.q monet:bollard.
+Ruleset one is then applied to the output of ruleset three,
+followed similarly by rulesets twenty-one and four.
+.pp
+If you need more detail,
+you can also use the
+.q \-d21
+flag to turn on more debugging.
+For example,
+.(b
+sendmail \-bt \-d21.99
+.)b
+turns on an incredible amount of information;
+a single word address
+is probably going to print out several pages worth of information.
+.pp
+You should be warned that internally,
+.i sendmail
+applies ruleset 3 to all addresses.
+In test mode
+you will have to do that manually.
+For example, older versions allowed you to use
+.(b
+0 bruce@broadcast.sony.com
+.)b
+This version requires that you use:
+.(b
+3,0 bruce@broadcast.sony.com
+.)b
+.pp
+As of version 8.7,
+some other syntaxes are available in test mode:
+.bu
+\&.D\|x\|value
+defines macro
+.i x
+to have the indicated
+.i value .
+This is useful when debugging rules that use the
+.b $& \c
+.i x
+syntax.
+.bu
+\&.C\|c\|value
+adds the indicated
+.i value
+to class
+.i c .
+.bu
+\&.S\|ruleset
+dumps the contents of the indicated ruleset.
+.bu
+\-d\|debug-spec
+is equivalent to the command-line flag.
 .sh 1 "TUNING"
 .pp
 There are a number of configuration parameters
@@ -1471,9 +1757,9 @@ Most of these are set
 using an option in the configuration file.
 For example,
 the line
-.q OT5d
+.q "O Timeout.queuereturn=5d"
 sets option
-.q T
+.q Timeout.queuereturn
 to the value
 .q 5d
 (five days).
@@ -1487,6 +1773,22 @@ sites experiencing a large number of small messages,
 many of which are delivered to many recipients,
 may find that they need to adjust the parameters
 dealing with queue priorities.
+.pp
+All versions of
+.i sendmail
+prior to 8.7
+had single character option names.
+As of 8.7,
+options have long (multi-character names).
+Although old short names are still accepted,
+most new options do not have short equivalents.
+.pp
+This section only describes the options you are most likely
+to want to tweak;
+read section
+.\"XREF
+5
+for more details.
 .sh 2 "Timeouts"
 .pp
 All time intervals are set
@@ -1516,17 +1818,24 @@ and one hour.
 RFC 1123 section 5.3.1.1 recommends that this be at least 30 minutes.
 .sh 3 "Read timeouts"
 .pp
-It is possible to time out when reading the standard input
-or when reading from a remote SMTP server.
-These timeouts are set using the
-.b r
-option in the configuration file.
-The argument is a list of
-.i keyword=value
-pairs.
-The recognized keywords, their default values, and the minimum values
+Timeouts all have option names
+.q Timeout.\fIsuboption\fP .
+The recognized
+.i suboption s,
+their default values, and the minimum values
 allowed by RFC 1123 section 5.3.2 are:
 .nr ii 1i
+.ip connect
+The time to wait for an SMTP connection to open
+(the
+.i connect (2)
+system call)
+[0, unspecified].
+If zero, uses the kernel default.
+In no case can this option extend the timeout
+longer than the kernel provides, but it can shorten it.
+This is to get around kernels that provide an absurdly long connection timeout
+(90 minutes in one case).
 .ip initial
 The wait for the initial 220 greeting message
 [5m, 5m].
@@ -1543,7 +1852,8 @@ The wait for a reply from a RCPT command
 [1h, 5m].
 This should be long
 because it could be pointing at a list
-that takes a long time to expand.
+that takes a long time to expand
+(see below).
 .ip datainit\(dg
 The wait for a reply from a DATA command
 [5m, 2m].
@@ -1578,10 +1888,15 @@ the time to wait for another command.
 [1h, 5m].
 .ip ident
 The timeout waiting for a reply to an IDENT query
-[30s, unspecified].
+[30s\**, unspecified].
+.(f
+\**On some systems the default is zero to turn the protocol off entirely.
+.)f
 .lp
 For compatibility with old configuration files,
-if no ``keyword='' is specified,
+if no
+.i suboption
+is specified,
 all the timeouts marked with \(dg are set to the indicated value.
 .pp
 Many of the RFC 1123 minimum values
@@ -1589,15 +1904,15 @@ may well be too short.
 .i Sendmail
 was designed to the RFC 822 protocols,
 which did not specify read timeouts;
-hence,
+hence, versions of
 .i sendmail
-does not guarantee to reply to messages promptly.
+prior to version 8.1 did not guarantee to reply to messages promptly.
 In particular, a
 .q RCPT
 command specifying a mailing list
 will expand and verify the entire list;
 a large list on a slow system
-may take more than five minutes\**.
+may easily take more than five minutes\**.
 .(f
 \**This verification includes looking up every address
 with the name server;
@@ -1605,13 +1920,15 @@ this involves network delays,
 and can in some cases can be considerable.
 .)f
 I recommend a one hour timeout \*-
-since this failure is rare,
+since a communications failure during the RCPT phase is rare,
 a long timeout is not onerous
-and may ultimately help reduce network load.
+and may ultimately help reduce network load
+and duplicated messages.
 .pp
-For example, the line:
+For example, the lines:
 .(b
-Orcommand=25m,datablock=3h
+O Timeout.command=25m
+O Timeout.datablock=3h
 .)b
 sets the server SMTP command timeout to 25 minutes
 and the input data block timeout to three hours.
@@ -1621,28 +1938,25 @@ After sitting in the queue for a few days,
 a message will time out.
 This is to insure that at least the sender is aware
 of the inability to send a message.
-The timeout is typically set to three days.
-This timeout is set using the
+The timeout is typically set to five days.
+It is sometimes considered convenient to also send a warning message
+if the message is in the queue longer than a few hours
+(assuming you normally have good connectivity;
+if your messages normally took several hours to send
+you wouldn't want to do this because it wouldn't be an unusual event).
+These timeouts are set using the
+.b Timeout.queuereturn
+and
+.b Timeout.queuewarn
+options in the configuration file
+(previously both were set using the
 .b T
-option in the configuration file.
-.pp
-The time of submission is set in the queue,
-rather than the amount of time left until timeout.
-As a result, you can flush messages that have been hanging
-for a short period
-by running the queue
-with a short message timeout.
-For example,
-.(b
-/usr/\*(SD/sendmail \-oT1d \-q
-.)b
-will run the queue
-and flush anything that is one day old.
+option).
 .pp
-Since this option is global,
-and since you can not
+Since these options are global,
+and since you can not know
 .i "a priori"
-know how long another host outside your domain will be down,
+how long another host outside your domain will be down,
 a five day timeout is recommended.
 This allows a recipient to fix the problem even if it occurs
 at the beginning of a long weekend.
@@ -1650,13 +1964,15 @@ RFC 1123 section 5.3.1.1 says that this parameter
 should be ``at least 4\-5 days''.
 .pp
 The
+.b Timeout.queuewarn
+value can be piggybacked on the
 .b T
-option can also take a second timeout indicating a time after which
+option by indicating a time after which
 a warning message should be sent;
 the two timeouts are separated by a slash.
-For example, the value
+For example, the line
 .(b
-5d/4h
+OT5d/4h
 .)b
 causes email to fail after five days,
 but a warning message will be sent after four hours.
@@ -1665,7 +1981,9 @@ several times.
 .sh 2 "Forking During Queue Runs"
 .pp
 By setting the
-.b Y
+.b ForkEachJob
+(\c
+.b Y )
 option,
 .i sendmail
 will fork before each individual message
@@ -1675,14 +1993,14 @@ This will prevent
 from consuming large amounts of memory,
 so it may be useful in memory-poor environments.
 However, if the
-.b Y
+.b ForkEachJob
 option is not set,
 .i sendmail
 will keep track of hosts that are down during a queue run,
 which can improve performance dramatically.
 .pp
 If the
-.b Y
+.b ForkEachJob
 option is set,
 .i sendmail
 can not use connection caching.
@@ -1690,7 +2008,9 @@ can not use connection caching.
 .pp
 Every message is assigned a priority when it is first instantiated,
 consisting of the message size (in bytes)
-offset by the message class times the
+offset by the message class
+(which is determined from the Precedence: header)
+times the
 .q "work class factor"
 and the number of recipients times the
 .q "work recipient factor."
@@ -1714,16 +2034,20 @@ this is also included into the priority.
 .pp
 The recipient and class factors
 can be set in the configuration file using the
-.b y
+.b RecipientFactor
+(\c
+.b y )
 and
-.b z
+.b ClassFactor
+(\c
+.b z )
 options respectively.
 They default to 30000 (for the recipient factor)
 and 1800
 (for the class factor).
 The initial priority is:
 .EQ
-pri = msgsize - (class times bold z) + (nrcpt times bold y)
+pri = msgsize - (class times bold ClassFactor) + (nrcpt times bold RecipientFactor)
 .EN
 (Remember, higher values for this parameter actually mean
 that the job will be treated with lower priority.)
@@ -1733,14 +2057,16 @@ The priority of a job can also be adjusted each time it is processed
 using the
 .q "work time factor,"
 set by the
-.b Z
+.b RetryFactor
+(\c
+.b Z )
 option.
 This is added to the priority,
 so it normally decreases the precedence of the job,
 on the grounds that jobs that have failed many times
 will tend to fail again in the future.
 The
-.b Z
+.b RetryFactor
 option defaults to 90000.
 .sh 2 "Load Limiting"
 .pp
@@ -1748,30 +2074,31 @@ option defaults to 90000.
 can be asked to queue (but not deliver)
 mail if the system load average gets too high
 using the
-.b x
+.b QueueLA
+(\c
+.b x )
 option.
 When the load average exceeds the value of the
-.b x
+.b QueueLA
 option,
 the delivery mode is set to
 .b q
 (queue only)
 if the
-.i "Queue Factor"
+.b QueueFactor
 (\c
-.b q
-option)
-divided by the difference in the current load average and the
-.b x
+.b q )
+option divided by the difference in the current load average and the
+.b QueueLA
 option
 plus one
 exceeds the priority of the message \(em
 that is, the message is queued iff:
 .EQ
-pri > { bold q } over { LA - { bold x } + 1 }
+pri > { bold QueueFactor } over { LA - { bold QueueLA } + 1 }
 .EN
 The
-.b q
+.b QueueFactor
 option defaults to 600000,
 so each point of load average is worth 600000
 priority points
@@ -1779,7 +2106,9 @@ priority points
 .pp
 For drastic cases,
 the
-.b X
+.b RefuseLA
+(\c
+.b X )
 option defines a load average at which
 .i sendmail
 will refuse
@@ -1793,7 +2122,9 @@ There are a number of delivery modes that
 .i sendmail
 can operate in,
 set by the
-.q d
+.b DeliveryMode
+(\c
+.b d )
 configuration option.
 These modes
 specify how quickly mail will be delivered.
@@ -1803,25 +2134,42 @@ Legal modes are:
 i      deliver interactively (synchronously)
 b      deliver in background (asynchronously)
 q      queue only (don't deliver)
+d      defer delvery attempts (don't deliver)
 .)b
 There are tradeoffs.
 Mode
 .q i
-passes the maximum amount of information to the sender,
-but is hardly ever necessary.
+gives the sender the quickest feedback,
+but may slow down some mailers and
+is hardly ever necessary.
+Mode
+.q b
+delivers promptly but
+can cause large numbers of processes
+if you have a mailer that takes a long time to deliver a message.
 Mode
 .q q
-puts the minimum load on your machine,
+minimizes the load on your machine,
 but means that delivery may be delayed for up to the queue interval.
 Mode
+.q d
+is identical to mode
+.q q
+except that it also prevents all the early map lookups from working;
+it is intended for ``dial on demand'' sites where DNS lookups
+might cost real money.
+Some simple error messages
+(e.g., host unknown during the SMTP protocol)
+will be delayed using this mode.
+Mode
 .q b
-is probably a good compromise.
-However, this mode can cause large numbers of processes
-if you have a mailer that takes a long time to deliver a message.
+is the usual default.
 .pp
 If you run in mode
 .q q
-(queue only)
+(queue only),
+.q d
+(defer),
 or
 .q b
 (deliver in background)
@@ -1829,6 +2177,9 @@ or
 will not expand aliases and follow .forward files
 upon initial receipt of the mail.
 This speeds up the response to RCPT commands.
+Mode
+.q i
+cannot be used by the SMTP server.
 .sh 2 "Log Level"
 .pp
 The level of logging can be set for
@@ -1854,14 +2205,12 @@ VRFY and EXPN commands.
 .ip 7
 Delivery failures (host or user unknown, etc.).
 .ip 8
-Successful deliveries.
+Successful deliveries and alias database rebuilds.
 .ip 9
 Messages being deferred
 (due to a host being down, etc.).
 .ip 10
 Database expansion (alias, forward, and userdb lookups).
-.ip 15
-Automatic alias database rebuilds.
 .ip 20
 Logs attempts to run locked queue files.
 These are not errors,
@@ -1870,13 +2219,11 @@ but can be useful to note if your queue appears to be clogged.
 Lost locks (only if using lockf instead of flock).
 .lp
 Additionally,
-values above 64 are reserved for extremely verbose debuggging output.
+values above 64 are reserved for extremely verbose debugging output.
 No normal site would ever set these.
 .sh 2 "File Modes"
 .pp
-There are a number of files
-that may have a number of modes.
-The modes depend on what functionality you want
+The modes used for files depend on what functionality you want
 and the level of security you require.
 .sh 3 "To suid or not to suid?"
 .pp
@@ -1907,6 +2254,26 @@ to be accounted
 .i sa \|(8))
 to root
 rather than to the user sending the mail.
+.pp
+If you don't make
+.i sendmail
+setuid to root, it will still run but you lose a lot of functionality
+and a lot of privacy, since you'll have to make the queue directory
+world readable.
+You could also make
+.i sendmail
+setuid to some pseudo-user
+(e.g., create a user called
+.q sendmail
+and make
+.i sendmail
+setuid to that)
+which will fix the privacy problems
+but not the functionality issues.
+Also, this isn't a guarantee of security:
+for example,
+root occasionally sends mail,
+and the daemon often runs as root.
 .sh 3 "Should my alias database be writable?"
 .pp
 At Berkeley
@@ -1954,7 +2321,7 @@ can arrange to steal mail anyway.
 If your DBM files are not writable by the world
 or you do not have auto-rebuild enabled
 (with the
-.q D
+.b AutoRebuildAliases
 option),
 then you must be careful to reconstruct the alias database
 each time you change the text version:
@@ -1982,7 +2349,9 @@ instead, the connection is closed and reopened.
 .pp
 Two parameters control the connection cache.
 The
-.b k
+.b ConnectionCacheSize
+(\c
+.b k )
 option defines the number of simultaneous open connections
 that will be permitted.
 If it is set to zero,
@@ -1992,9 +2361,12 @@ This should be set as appropriate for your system size;
 it will limit the amount of system resources that
 .i sendmail
 will use during queue runs.
+Never set this higher than 4.
 .pp
 The
-.b K
+.b ConnectionCacheTimeout
+(\c
+.b K )
 option specifies the maximum time that any cached connection
 will be permitted to idle.
 When the idle time exceeds this value
@@ -2006,39 +2378,65 @@ from other hosts.
 The default is five minutes.
 .sh 2 "Name Server Access"
 .pp
-If your system supports the name server,
-then the probability is that
+Control of host address lookups is set by the
+.b hosts
+service entry in your service switch file.
+If you are on a system that has built-in service switch support
+(e.g., Ultrix, Solaris, or DEC OSF/1)
+then your system is probably configured properly already.
+Otherwise,
 .i sendmail
-will be using it regardless of how you configure
-.i sendmail .
+will consult the file
+.b /etc/service.switch ,
+which should be created.
+.i Sendmail
+only uses two entries:
+.b hosts
+and
+.b aliases .
+.pp
+However, some systems (such as SunOS)
+will do DNS lookups
+regardless of the setting of the service switch entry.
 In particular, the system routine
 .i gethostbyname (3)
 is used to look up host names,
-and most vendor versions try some combination of DNS, NIS,
-and file lookup in /etc/hosts.
-.pp
-However, if you do not have a nameserver configured at all,
+and many vendor versions try some combination of DNS, NIS,
+and file lookup in /etc/hosts
+without consulting a service switch.
+.i Sendmail
+makes no attempt to work around this problem,
+and the DNS lookup will be done anyway.
+If you do not have a nameserver configured at all,
 such as at a UUCP-only site,
 .i sendmail
 will get a
 .q "connection refused"
-message when it tries to connect to the name server
-(either indirectly by calling
-.i gethostbyname
-or directly by looking up MX records).
+message when it tries to connect to the name server.
 If the
-.b I
-option is set,
+.b hosts
+switch entry has the service
+.q dns
+listed somewhere in the list,
 .i sendmail
 will interpret this to mean a temporary failure
 and will queue the mail for later processing;
 otherwise, it ignores the name server data.
-If your name server is running properly,
-the setting of this option is not relevant;
-however, it is important that it be set properly
-to make error handling work properly.
 .pp
-This option also allows you to tweak name server options.
+The same technique is used to decide whether to do MX lookups.
+If you want MX support, you
+.i must
+have
+.q dns
+listed as a service in the
+.b hosts
+switch entry.
+.pp
+The
+.b ResolverOptions
+(\c
+.b I )
+option allows you to tweak name server options.
 The command line takes a series of flags as documented in
 .i resolver (3)
 (with the leading
@@ -2047,16 +2445,17 @@ deleted).
 Each can be preceded by an optional `+' or `\(mi'.
 For example, the line
 .(b
-OITrue +AAONLY \(miDNSRCH
+O ResolverOptions=+AAONLY \(miDNSRCH
 .)b
 turns on the AAONLY (accept authoritative answers only)
 and turns off the DNSRCH (search the domain path) options.
 Most resolver libraries default DNSRCH, DEFNAMES, and RECURSE
 flags on and all others off.
-Note the use of the initial ``True'' \*-
-this is for compatibility with previous versions of
-.i sendmail ,
-but is not otherwise necessary.
+You can also include
+.q HasWildcardMX
+to specify that there is a wildcard MX record matching your domain;
+this turns off MX matching when canonifying names,
+which can lead to inappropriate canonifications.
 .pp
 Version level 1 configurations
 turn DNSRCH and DEFNAMES off when doing delivery lookups,
@@ -2070,7 +2469,7 @@ If you don't want to do automatic name extension,
 don't call $[ ... $].
 .pp
 The search rules for $[ ... $] are somewhat different than usual.
-If the name (that is, the ``...'')
+If the name being looked up
 has at least one dot, it always tries the unmodified name first.
 If that fails, it tries the reduced search path,
 and lastly tries the unmodified name
@@ -2085,6 +2484,14 @@ that is, if it finds an MX record it makes note of it,
 but keeps looking.
 This way, if you have a wildcard MX record matching your domain,
 it will not assume that all names match.
+.pp
+To completely turn off all name server access
+on systems without service switch support
+(such as SunOS)
+you will have to recompile with
+\-DNAMED_BIND=0
+and remove \-lresolv from the list of libraries to be searched
+when linking.
 .sh 2 "Moving the Per-User Forward Files"
 .pp
 Some sites mount each user's home directory
@@ -2097,17 +2504,20 @@ because of a file server being down.
 The performance can be especially bad if you run the automounter.
 .pp
 The
-.b J
+.b ForwardPath
+(\c
+.b J )
 option allows you to set a path of forward files.
 For example, the config file line
 .(b
-OJ/var/forward/$u:$z/.forward
+O ForwardPath=/var/forward/$u:$z/.forward.$w
 .)b
 would first look for a file with the same name as the user's login
 in /var/forward;
 if that is not found (or is inaccessible)
 the file
-.q \&.forward
+``.forward.\c
+.i machinename ''
 in the user's home directory is searched.
 A truly perverse site could also search by sender
 by using $r, $s, or $f.
@@ -2118,12 +2528,18 @@ it should be mode 1777
 Users should create the files mode 644.
 .sh 2 "Free Space"
 .pp
-On systems that have the
+On systems that have one of the system calls in the
 .i statfs (2)
-system call,
+family
+(including
+.i statvfs
+and
+.i ustat ),
 you can specify a minimum number of free blocks on the queue filesystem
 using the
-.b b
+.b MinFreeBlocks
+(\c
+.b b )
 option.
 If there are fewer than the indicated number of blocks free
 on the filesystem on which the queue is mounted
@@ -2135,43 +2551,42 @@ This invites the SMTP client to try again later.
 Beware of setting this option too high;
 it can cause rejection of email
 when that mail would be processed without difficulty.
+.sh 2 "Maximum Message Size"
 .pp
-This option can also specify an advertised
-.q "maximum message size"
-for hosts that speak ESMTP.
+To avoid overflowing your system with a large message,
+the
+.b MaxMessageSize
+option can be set to set an absolute limit
+on the size of any one message.
+This will be advertised in the ESMTP dialogue
+and checked during message collection.
 .sh 2 "Privacy Flags"
 .pp
 The
-.b p
+.b PrivacyOptions
+(\c
+.b p )
 option allows you to set certain
 ``privacy''
 flags.
 Actually, many of them don't give you any extra privacy,
 rather just insisting that client SMTP servers
 use the HELO command
-before using certain commands.
+before using certain commands
+or adding extra headers to indicate possible spoof attempts.
 .pp
 The option takes a series of flag names;
 the final privacy is the inclusive or of those flags.
 For example:
 .(b
-Oneedmailhelo, noexpn
+O PrivacyOptions=needmailhelo, noexpn
 .)b
 insists that the HELO or EHLO command be used before a MAIL command is accepted
 and disables the EXPN command.
 .pp
-The
-.q restrictmailq
-option restricts printing the queue to the group that owns the queue directory.
-It is absurd to set this if you don't also protect the logs.
-.pp
-The
-.q restrictqrun
-option restricts people running the queue
-(that is, using the
-.b \-q
-command line flag)
-to root and the owner of the queue directory.
+The flags are detailed in section
+.\"XREF
+5.1.6.
 .sh 2 "Send to Me Too"
 .pp
 Normally,
@@ -2186,9 +2601,11 @@ If the
 .b \-m
 (me too)
 command line flag, or if the
-.b m
+.b MeToo
+(\c
+.b m )
 option is set in the configuration file,
-this behaviour is supressed.
+this behaviour is suppressed.
 Some sites like to run the
 .sm SMTP
 daemon with
@@ -2196,9 +2613,7 @@ daemon with
 .sh 1 "THE WHOLE SCOOP ON THE CONFIGURATION FILE"
 .pp
 This section describes the configuration file
-in detail,
-including hints on how to write one of your own
-if you have to.
+in detail.
 .pp
 There is one point that should be made clear immediately:
 the syntax of the configuration file
@@ -2212,11 +2627,6 @@ On the
 list is a
 configuration-file compiler.
 .pp
-An overview of the configuration file
-is given first,
-followed by details of the semantics.
-.sh 2 "Configuration File Lines"
-.pp
 The configuration file is organized as a series of lines,
 each of which begins with a single character
 defining the semantics for the rest of the line.
@@ -2226,7 +2636,7 @@ are continuation lines
 Blank lines and lines beginning with a sharp symbol
 (`#')
 are comments.
-.sh 3 "R and S \*- rewriting rules"
+.sh 2 "R and S \*- Rewriting Rules"
 .pp
 The core of address parsing
 are the rewriting rules.
@@ -2291,7 +2701,7 @@ This for is intended only for referencing internally defined macros
 such as
 .b $h
 that are changed at runtime.
-.sh 4 "The left hand side"
+.sh 3 "The left hand side"
 .pp
 The left hand side of rewriting rules contains a pattern.
 Normal words are simply matched directly.
@@ -2336,10 +2746,10 @@ This is
 .i not
 bound to a
 .b $ \c
-.i N
+.i n
 on the RHS, and is normally only used when it stands alone
 in order to match the null input.
-.sh 4 "The right hand side"
+.sh 3 "The right hand side"
 .pp
 When the left hand side of a rewriting rule matches,
 the input is deleted and replaced by the right hand side.
@@ -2375,9 +2785,8 @@ A host name enclosed between
 .b $[
 and
 .b $]
-is looked up using the
-.i gethostent \|(3)
-routines and replaced by the canonical name\**.
+is looked up in the host database(s)
+and replaced by the canonical name\**.
 .(f
 \**This is actually
 completely equivalent
@@ -2387,9 +2796,9 @@ In particular, a
 default can be used.
 .)f
 For example,
-.q $[csam$]
+.q $[ftp$]
 might become
-.q lbl-csam.arpa
+.q ftp.CS.Berkeley.EDU
 and
 .q $[[128.32.130.2]$]
 would become
@@ -2410,6 +2819,9 @@ If no lookup is found, the indicated
 is inserted;
 if no default is specified and no lookup matches,
 the value is left unchanged.
+The
+.i arguments
+are passed to the map for possible use.
 .pp
 The
 .b $> \c
@@ -2422,6 +2834,13 @@ The final value of ruleset
 .i n
 then becomes
 the substitution for this rule.
+The
+.b $>
+syntax can only be used at the beginning of the right hand side;
+it can be only be preceded by
+.b $@
+or
+.b $: .
 .pp
 The
 .b $#
@@ -2446,10 +2865,11 @@ the host part may be omitted\**.
 \**You may want to use it for special
 .q "per user"
 extensions.
-For example, at CMU you can send email to
-.q jgm+foo ;
-the part after the plus sign
-is not part of the user name,
+For example, in the address
+.q jgm+foo@CMU.EDU ;
+the
+.q +foo
+part is not part of the user name,
 and is passed to the local mailer for local use.
 .)f
 The
@@ -2537,11 +2957,11 @@ and finally
 and
 .b $:
 are processed.
-.sh 4 "Semantics of rewriting rule sets"
+.sh 3 "Semantics of rewriting rule sets"
 .pp
 There are five rewriting sets
 that have specific semantics.
-These are related as depicted by figure 2.
+Four of these are related as depicted by figure 1.
 .(z
 .hl
 .ie n \{\
@@ -2592,7 +3012,7 @@ Box0:     arrow; box "0"
 .\}
 .el .sp 2i
 .ce
-Figure 2 \*- Rewriting set semantics
+Figure 1 \*- Rewriting set semantics
 .(c
 D \*- sender domain addition
 S \*- mailer-specific sender rewriting
@@ -2608,13 +3028,21 @@ This form should have the basic syntax:
 .(b
 local-part@host-domain-spec
 .)b
+Ruleset three
+is applied by
+.i sendmail
+before doing anything with any address.
+.pp
 If no
 .q @
 sign is specified,
 then the
 host-domain-spec
 .i may
-be appended from the
+be appended (box
+.q D
+in Figure 1)
+from the
 sender address
 (if the
 .b C
@@ -2622,10 +3050,6 @@ flag is set in the mailer definition
 corresponding to the
 .i sending
 mailer).
-Ruleset three
-is applied by
-.i sendmail
-before doing anything with any address.
 .pp
 Ruleset zero
 is applied after ruleset three
@@ -2654,7 +3078,7 @@ Ruleset four is applied to all addresses
 in the message.
 It is typically used
 to translate internal to external form.
-.sh 4 "IPC mailers"
+.sh 3 "IPC mailers"
 .pp
 Some special processing occurs
 if the ruleset zero resolves to an IPC mailer
@@ -2694,21 +3118,28 @@ in square brackets:
 .)b
 This form avoids the MX mapping.
 .b N.B.:
-This is intended only for situations where you have a network firewall,
+.i
+This is intended only for situations where you have a network firewall
+or other host that will do special processing for all your mail,
 so that your MX record points to a gateway machine;
 this machine could then do direct delivery to machines
 within your local domain.
 Use of this feature directly violates RFC 1123 section 5.3.5:
 it should not be used lightly.
-.sh 3 "D \*- define macro"
+.r
+.sh 2 "D \*- Define Macro"
 .pp
-Macros are named with a single character.
-These may be selected from the entire ASCII set,
+Macros are named with a single character
+or with a word in {braces}.
+Single character names may be selected from the entire ASCII set,
 but user-defined macros
 should be selected from the set of upper case letters only.
 Lower case letters
 and special symbols
 are used internally.
+Long names beginning with a lower case letter or a punctuation character
+are reserved for use by sendmail,
+so user-defined long macro names should begin with an upper case letter.
 .pp
 The syntax for macro definitions is:
 .(b F
@@ -2718,9 +3149,13 @@ The syntax for macro definitions is:
 where
 .i x
 is the name of the macro
+(which may be a single character
+or a word in braces)
 and
 .i val
 is the value it should have.
+There should be no spaces given
+that do not actually belong in the macro value.
 .pp
 Macros are interpolated
 using the construct
@@ -2787,15 +3222,21 @@ but are not otherwise used internally.
 These macros are:
 .nr ii 5n
 .ip $a
-.b "The origination date in RFC 822 format."
+The origination date in RFC 822 format.
+This is extracted from the Date: line.
 .ip $b
-.b "The current date in RFC 822 format."
+The current date in RFC 822 format.
 .ip $c
-.b "The hop count."
+The hop count.
+This is a count of the number of Received: lines
+plus the value of the
+.b \-h
+command line flag.
 .ip $d
-.b "The current date in UNIX (ctime) format."
+The current date in UNIX (ctime) format.
 .ip $e\(dg
-.b "The SMTP entry message."
+(Obsolete; use SmtpGreetingMessage option instead.)
+The SMTP entry message.
 This is printed out when SMTP starts up.
 The first word must be the
 .b $j
@@ -2805,15 +3246,27 @@ Defaults to
 Commonly redefined to include the configuration version number, e.g.,
 .q "$j Sendmail $v/$Z ready at $b"
 .ip $f
-.b "The sender (from) address."
+The envelope sender (from) address.
 .ip $g
-.b "The sender address relative to the recipient."
+The sender address relative to the recipient.
+For example, if
+.b $f
+is
+.q foo ,
+.b $g
+will be
+.q host!foo ,
+.q foo@host.domain ,
+or whatever is appropriate for the receiving mailer.
 .ip $h
-.b "The recipient host."
+The recipient host.
+This is set in ruleset 0 from the $# field of a parsed address.
 .ip $i
-.b "The queue id."
+The queue id,
+e.g.,
+.q HAA12345 .
 .ip $j\(dd
-.b "The \*(lqofficial\*(rq domain name for this site."
+The \*(lqofficial\*(rq domain name for this site.
 This is fully qualified if the full qualification can be found.
 It
 .i must
@@ -2821,25 +3274,27 @@ be redefined to be the fully qualified domain name
 if your system is not configured so that information can find
 it automatically.
 .ip $k
-.b "The UUCP node name (from the uname system call)."
+The UUCP node name (from the uname system call).
 .ip $l\(dg
-.b "The format of the UNIX from line."
+(Obsolete; use UnixFromLine option instead.)
+The format of the UNIX from line.
 Unless you have changed the UNIX mailbox format,
 you should not change the default,
 which is
 .q "From $g  $d" .
 .ip $m
-.b "The domain part of the \fIgethostname\fP return value."
+The domain part of the \fIgethostname\fP return value.
 Under normal circumstances,
 .b $j
 is equivalent to
 .b $w.$m .
 .ip $n\(dg
-.b "The name of the daemon (for error messages)."
+The name of the daemon (for error messages).
 Defaults to
 .q MAILER-DAEMON .
 .ip $o\(dg
-.b "The set of \*(lqoperators\*(rq in addresses."
+(Obsolete: use OperatorChars option instead.)
+The set of \*(lqoperators\*(rq in addresses.
 A list of characters
 which will be considered tokens
 and which will separate tokens
@@ -2862,9 +3317,9 @@ a richer set of operators is
 .q ".:%@!/[]" ,
 which adds support for UUCP, the %-hack, and X.400 addresses.
 .ip $p
-.b "Sendmail's process id."
+Sendmail's process id.
 .ip $q\(dg
-.b "Default format of sender address."
+Default format of sender address.
 The
 .b $q
 macro specifies how an address should appear in a message
@@ -2884,27 +3339,32 @@ eric@CS.Berkeley.EDU (Eric Allman)
 properly quotes names that have special characters
 if the first form is used.
 .ip $r
-.b "Protocol used to receive the message."
+Protocol used to receive the message.
+Set from the
+.b \-p
+command line flag or by the SMTP server code.
 .ip $s
-.b "Sender's host name."
+Sender's host name.
+Set from the
+.b \-p
+command line flag or by the SMTP server code.
 .ip $t
-.b "A numeric representation of the current time."
+A numeric representation of the current time.
 .ip $u
-.b "The recipient user."
+The recipient user.
 .ip $v
-.b "The version number of \fIsendmail\fP."
+The version number of the
+.i sendmail
+binary.
 .ip $w\(dd
-.b "The hostname of this site."
-.pp
-The
-.b $w
-macro is set to the root name of this host (but see below for caveats).
+The hostname of this site.
+This is the root name of this host (but see below for caveats).
 .ip $x
-.b "The full name of the sender."
+The full name of the sender.
 .ip $z
-.b "The home directory of the recipient."
+The home directory of the recipient.
 .ip $_
-.b "The validated sender address."
+The validated sender address.
 .pp
 There are three types of dates that can be used.
 The
@@ -3017,10 +3477,13 @@ macro is set to the full name of the sender.
 This can be determined in several ways.
 It can be passed as flag to
 .i sendmail .
-The second choice is the value of the
-.q Full-name:
+It can be defined in the
+.sm NAME
+environment variable.
+The third choice is the value of the
+.q Full-Name:
 line in the header if it exists,
-and the third choice is the comment field
+and the fourth choice is the comment field
 of a
 .q From:
 line.
@@ -3083,13 +3546,21 @@ and
 fields are set to the protocol used to communicate with
 .i sendmail
 and the sending hostname.
+They can be set together using the
+.b \-p
+command line flag or separately using the
+.b \-M
+or
+.b \-oM
+flags.
 .pp
 The
 .b $_
 is set to a validated sender host name.
-If the sender is running an RFC 1413 compliant IDENT server,
+If the sender is running an RFC 1413 compliant IDENT server
+and the receiver has the IDENT protocol turned on,
 it will include the user name on that host.
-.sh 3 "C and F \*- define classes"
+.sh 2 "C and F \*- Define Classes"
 .pp
 Classes of phrases may be defined
 to match on the left hand side of rewriting rules,
@@ -3103,10 +3574,12 @@ so that attempts to send to oneself
 can be eliminated.
 These can either be defined directly in the configuration file
 or read in from another file.
-Classes may be given names
-from the set of upper case letters.
-Lower case letters and special characters
-are reserved for system use.
+Classes are named as a single letter or a word in {braces}.
+Class names beginning with lower case letters
+and special characters are reserved for system use.
+Classes defined in config files may be given names
+from the set of upper case letters for short names
+or beginning with an upper case letter for long names.
 .pp
 The syntax is:
 .(b F
@@ -3131,12 +3604,16 @@ CHmonet
 CHucbmonet
 .)b
 are equivalent.
-The second form
+The ``F'' form
 reads the elements of the class
 .i c
 from the named
 .i file .
 .pp
+Elements of classes can be accessed in rules using
+.b $=
+or
+.b $~ .
 The
 .b $~
 (match entries not in class)
@@ -3161,6 +3638,50 @@ is set to the set of domains by which this host is known,
 initially just
 .b $m .
 .pp
+The class
+.b $=t
+is set to the set of trusted users by the
+.b T
+configuration line.
+If you want to read trusted users from a file use
+.b Ft \c
+.i /file/name .
+.pp
+The class
+.b $=n
+can be set to the set of MIME body types
+that can never be eight to seven bit encoded.
+It defaults to
+.q multipart/signed .
+Message types
+.q message/*
+and
+.q multipart/*
+are never encoded directly.
+Multipart messages are always handled recursively.
+The handling of message/* messages
+are controlled by class
+.b $=s .
+The class
+.b $=e
+contains the Content-Transfer-Encodings that can be 8\(->7 bit encoded.
+It is predefined to contain
+.q 7bit ,
+.q 8bit ,
+and
+.q binary .
+The class
+.b $=s
+contains the set of subtypes of message that can be treated recursively.
+By default it contains only
+.q rfc822 .
+Other
+.q message/*
+types cannot be 8\(->7 bit encoded.
+If a message containing eight bit data is sent to a seven bit host,
+and that message cannot be encoded into seven bits,
+it will be stripped to 7 bits.
+.pp
 .i Sendmail
 can be compiled to allow a
 .i scanf (3)
@@ -3175,7 +3696,7 @@ file into a class, use
 FL/etc/passwd %[^:]
 .)b
 which reads every line up to the first colon.
-.sh 3 "M \*- define mailer"
+.sh 2 "M \*- Define Mailer"
 .pp
 Programs and interfaces to mailers
 are defined in this line.
@@ -3199,13 +3720,17 @@ Fields are:
 .ta 1i
 Path   The pathname of the mailer
 Flags  Special flags for this mailer
-Sender A rewriting set for sender addresses
-Recipient      A rewriting set for recipient addresses
+Sender Rewriting set(s) for sender addresses
+Recipient      Rewriting set(s) for recipient addresses
 Argv   An argument vector to pass to this mailer
 Eol    The end-of-line string for this mailer
 Maxsize        The maximum message length to this mailer
 Linelimit      The maximum line length in the message body
 Directory      The working directory for the mailer
+Userid The default user and group id to run as
+Nice   The nice(2) increment for the mailer
+Charset        The default character set for 8-bit characters
+Type   The MTS type information (used for error messages)
 .)b
 Only the first character of the field name is checked.
 .pp
@@ -3213,9 +3738,25 @@ The following flags may be set in the mailer description.
 Any other flags may be used freely
 to conditionally assign headers to messages
 destined for particular mailers.
+Flags marked with \(dg
+are not interpreted by the
+.i sendmail
+binary;
+these are the conventionally used to correlate to the flags portion
+of the
+.b H
+line.
+Flags marked with \(dd
+apply to the mailers for the sender address
+rather than the usual recipient mailers.
 .nr ii 4n
 .ip a
-Run Extended SMTP (ESMTP) protocol (defined in RFCs 1425, 1426, and 1427).
+Run Extended SMTP (ESMTP) protocol (defined in RFCs 1651, 1652, and 1653).
+This flag defaults on if the SMTP greeting message includes the word
+.q ESMTP .
+.ip A
+Look up the user part of the address in the alias database.
+Normally this is only set for local mailers.
 .ip b
 Force a blank line on the end of a message.
 This is intended to work around some stupid versions of
@@ -3226,7 +3767,13 @@ It would not normally be used on network mail.
 Do not include comments in addresses.
 This should only be used if you have to work around
 a remote mailer that gets confused by comments.
-.ip C
+This strips addresses of the form
+.q "Phrase <address>"
+or
+.q "address (Comment)"
+down to just
+.q address .
+.ip C\(dd
 If mail is
 .i received
 from a mailer with this flag set,
@@ -3236,7 +3783,7 @@ any addresses in the header that do not have an at sign
 after being rewritten by ruleset three
 will have the
 .q @domain
-clause from the sender
+clause from the sender envelope address
 tacked on.
 This allows mail with headers of the form:
 .(b
@@ -3249,7 +3796,8 @@ From: usera@hosta
 To: userb@hostb, userc@hosta
 .)b
 automatically.
-.ip D
+However, it doesn't really work reliably.
+.ip D\(dg
 This mailer wants a
 .q Date:
 header line.
@@ -3271,7 +3819,7 @@ but only if this is a network forward operation
 the mailer will give an error
 if the executing user
 does not have special permissions).
-.ip F
+.ip F\(dg
 This mailer wants a
 .q From:
 header line.
@@ -3279,11 +3827,7 @@ header line.
 Normally,
 .i sendmail
 sends internally generated email (e.g., error messages)
-using the null return address\**
-.(f
-\**Actually, this only applies to SMTP,
-which uses the ``MAIL FROM:<>'' command.
-.)f
+using the null return address
 as required by RFC 1123.
 However, some mailers don't accept a null return address.
 If necessary,
@@ -3309,6 +3853,19 @@ This option is not required
 (i.e.,
 if this option is omitted the transmission will still operate successfully,
 although perhaps not as efficiently as possible).
+.ip k
+Normally when
+.i sendmail
+connects to a host via SMTP,
+it checks to make sure that this isn't accidently the same host name
+as might happen if
+.i sendmail
+is misconfigured or if a long-haul network interface is set in loopback mode.
+This flag disables the loopback check.
+It should only be used under very unusual circumstances.
+.ip K
+Currently unimplemented.
+Reserved for chunking.
 .ip l
 This mailer is local
 (i.e.,
@@ -3334,7 +3891,7 @@ macro occurs in the
 part of the mailer definition,
 that field will be repeated as necessary
 for all qualifying users.
-.ip M
+.ip M\(dg
 This mailer wants a
 .q Message-Id:
 header line.
@@ -3342,6 +3899,23 @@ header line.
 Do not insert a UNIX-style
 .q From
 line on the front of the message.
+.ip o
+Always run as the owner of the recipient mailbox.
+Normally
+.i sendmail
+runs as the sender for locally generated mail
+or as
+.q daemon
+(actually, the user specified in the
+.b u
+option)
+when delivering network mail.
+The normal behaviour is required by most local mailers,
+which will not allow the envelope sender address
+to be set unless the mailer is running as daemon.
+This flag is ignored if the
+.b S
+flag is set.
 .ip p
 Use the route-addr style reverse-path in the SMTP
 .q "MAIL FROM:"
@@ -3350,7 +3924,7 @@ rather than just the return address;
 although this is required in RFC821 section 3.1,
 many hosts do not process reverse-paths properly.
 Reverse-paths are officially discouraged by RFC 1123.
-.ip P
+.ip P\(dg
 This mailer wants a
 .q Return-Path:
 line.
@@ -3361,7 +3935,7 @@ but sends a
 .b \-r
 flag.
 .ip s
-Strip quote characters off of the address
+Strip quote characters (" and \e) off of the address
 before calling the mailer.
 .ip S
 Don't reset the userid
@@ -3371,20 +3945,31 @@ where
 .i sendmail
 ran as root.
 This could be used to avoid forged addresses.
-This flag is suppressed if given from an
-.q unsafe
-environment
-(e.g, a user's mail.cf file).
+If the
+.b U=
+field is also specified,
+this flag causes the user id to always be set to that user and group
+(instead of leaving it as root).
 .ip u
 Upper case should be preserved in user names
 for this mailer.
 .ip U
-This mailer wants Unix-style
+This mailer wants UUCP-style
 .q From
-lines with the ugly UUCP-style
+lines with the ugly
 .q "remote from <host>"
 on the end.
-.ip x
+.ip w
+The user must have a valid account on this machine,
+i.e.,
+getpwnam
+must succeed.
+If not,
+the mail is bounced.
+This is required to get
+.q \&.forward
+capability.
+.ip x\(dg
 This mailer wants a
 .q Full-Name:
 header line.
@@ -3397,6 +3982,10 @@ will have an extra dot prepended
 (to be stripped at the other end).
 This insures that lines in the message containing a dot
 will not terminate the message prematurely.
+.ip 5
+If no aliases are found for this address,
+pass the address through ruleset 5 for possible alternate resolution.
+This is intended to forward the mail to an alternate delivery spot.
 .ip 7
 Strip all output to seven bits.
 This is the default if the
@@ -3409,6 +3998,35 @@ If the
 .b 7
 option is set, this is essentially always set,
 since the eighth bit was stripped on input.
+Note that this option will only impact messages
+that didn't have 8\(->7 bit MIME conversions performed.
+.ip 8
+If set,
+it is acceptable to send eight bit data to this mailer;
+the usual attempt to do 8\(->7 bit MIME conversions will be bypassed.
+.ip :
+Check addresses to see if they begin
+.q :include: ;
+if they do, convert them to the
+.q *include*
+mailer.
+.ip |
+Check addresses to see if they begin with a `|';
+if they do, convert them to the
+.q prog
+mailer.
+.ip /
+Check addresses to see if they begin with a `/';
+if they do, convert them to the
+.q *file*
+mailer.
+.ip @
+Look up addresses in the user database.
+.pp
+Configuration files prior to level 6
+assume the `A', `w', `5', `:', `|', `/', and `@' options
+on the mailer named
+.q local .
 .pp
 The mailer with the special name
 .q error
@@ -3428,7 +4046,7 @@ and the
 .q "Host unknown"
 exit status to be returned
 if the LHS matches.
-This mailer is only functional in ruleset zero.
+This mailer is only functional in rulesets zero or five.
 .pp
 The mailer named
 .q local
@@ -3452,8 +4070,8 @@ M*include*, P=/dev/null, F=su, A=INCLUDE
 .)b
 .pp
 The Sender and Recipient rewriting sets
-may either be a simple integer
-or may be two integers separated by a slash;
+may either be a simple ruleset id
+or may be two ids separated by a slash;
 if so, the first rewriting set is applied to envelope
 addresses
 and the second is applied to headers.
@@ -3471,10 +4089,69 @@ mailer,
 since some shells (such as
 .i csh )
 refuse to execute if they cannot read the home directory.
-Since the queue directory is not normally readable by normal users
+Since the queue directory is not normally readable by unprivileged users
 .i csh
 scripts as recipients can fail.
-.sh 3 "H \*- define header"
+.pp
+The Userid
+specifies the default user and group id to run as,
+overriding the
+.b DefaultUser
+option (q.v.).
+If the
+.b S
+mailer flag is also specified,
+this is the user and group to run as in all circumstances.
+This may be given as
+.i user:group
+to set both the user and group id;
+either may be an integer or a symbolic name to be looked up
+in the
+.i passwd
+and
+.i group
+files respectively.
+If only a symbolic user name is specified,
+the group id in the
+.i passwd
+file for that user is used as the group id.
+.pp
+The Charset field
+is used when converting a message to MIME;
+this is the character set used in the
+Content-Type: header.
+If this is not set, the
+.b DefaultCharset
+option is used,
+and if that is not set, the value
+.q unknown-8bit
+is used.
+.b WARNING:
+this field applies to the sender's mailer,
+not the recipient's mailer.
+For example, if the envelope sender address
+lists an address on the local network
+and the recipient is on an external network,
+the character set will be set from the Charset= field
+for the local network mailer,
+not that of the external network mailer.
+.pp
+The Type= field
+sets the type information
+used in MIME error messages
+as defined by
+RFC XXX
+(not yet published).
+It is actually three values separated by slashes:
+the MTA-type (that is, the description of how hosts are named),
+the address type (the description of e-mail addresses),
+and the diagnostic type (the description of error diagnostic codes).
+Each of these must be a registered value
+or begin with
+.q X\- .
+The default is
+.q dns/rfc822/smtp .
+.sh 2 "H \*- Define Header"
 .pp
 The format of the header lines that
 .i sendmail
@@ -3509,23 +4186,38 @@ it is reflected to the output
 regardless of these flags.
 .pp
 Some headers have special semantics
-that will be described below.
-.sh 3 "O \*- set option"
+that will be described later.
+.sh 2 "O \*- Set Option"
 .pp
 There are a number of
-.q random
+global
 options that
 can be set from a configuration file.
-Options are represented by single characters.
+Options are represented by full words;
+some are also representable as single characters
+for back compatibility.
 The syntax of this line is:
 .(b F
-.b O \c
-.i o\|value
+.b O \0
+.i option \c
+.b = \c
+.i value
 .)b
 This sets option
-.i o
+.i option
 to be
 .i value .
+Note that there
+.i must
+be a space between the letter `O' and the name of the option.
+An older version is:
+.(b F
+.b O \c
+.i o\|value
+.)b
+where the option
+.i o
+is a single character.
 Depending on the option,
 .i value
 may be a string, an integer,
@@ -3540,25 +4232,10 @@ the default is TRUE),
 or
 a time interval.
 .pp
-The options supported are:
+The options supported (with the old, one character names in brackets) are:
 .nr ii 1i
-.ip a\fIN\fP
-If set,
-wait up to
-.i N
-minutes for an
-.q @:@
-entry to exist in the alias database
-before starting up.
-If it does not appear in
-.i N
-minutes,
-rebuild the database
-(if the
-.b D
-option is also set)
-or issue a warning.
-.ip "A\fIspec, spec, ...\fP"
+.ip "AliasFile=\fIspec, spec, ...\fP"
+[A]
 Specify possible alias file(s).
 Each
 .i spec
@@ -3598,32 +4275,46 @@ If a list of
 are provided,
 .i sendmail
 searches them in order.
-.ip b\fIN\fP/\fIM\fP
-Insist on at least
-.i N
-blocks free on the filesystem that holds the queue files
-before accepting email via SMTP.
-If there is insufficient space
+.ip AliasWait=\fItimeout\fP
+[a]
+If set,
+wait up to
+.i timeout
+(units default to minutes)
+for an
+.q @:@
+entry to exist in the alias database
+before starting up.
+If it does not appear in the
+.i timeout
+interval
+rebuild the database
+(if the
+.b AutoRebuildAliases
+option is also set)
+or issue a warning.
+.ip AutoRebuildAliases
+[D]
+If set,
+rebuild the alias database if necessary and possible.
+If this option is not set,
 .i sendmail
-gives a 452 response
-to the MAIL command.
-This invites the sender to try again later.
-The optional
-.i M
-is a maximum message size advertised in the ESMTP EHLO response.
-It is currently otherwise unused.
-.ip B\fIc\fP
+will never rebuild the alias database
+unless explicitly requested
+using
+.b \-bi .
+Not recommended \(em can cause thrashing.
+.ip BlankSub=\fIc\fP
+[B]
 Set the blank substitution character to
 .i c .
 Unquoted spaces in addresses are replaced by this character.
 Defaults to space (i.e., no change is made).
-.ip c
-If an outgoing mailer is marked as being expensive,
-don't connect immediately.
-This requires that queueing be compiled in,
-since it will depend on a queue run process to
-actually send the mail.
-.ip C\fIN\fP
+.ip CheckAliases
+[n]
+Validate the RHS of aliases when rebuilding the alias database.
+.ip CheckpointInterval=\fIN\fP
+[C]
 Checkpoints the queue every
 .i N
 (default 10)
@@ -3632,42 +4323,258 @@ If your system crashes during delivery to a large list,
 this prevents retransmission to any but the last
 .I N
 recipients.
-.ip d\fIx\fP
-Deliver in mode
-.i x .
-Legal modes are:
-.(b
-.ta 4n
-i      Deliver interactively (synchronously)
-b      Deliver in background (asynchronously)
-q      Just queue the message (deliver during queue run)
-.)b
-Defaults to ``b'' if no option is specified,
-``i'' if it is specified but given no argument
-(i.e., ``Od'' is equivalent to ``Odi'').
-.ip D
-If set,
-rebuild the alias database if necessary and possible.
-If this option is not set,
+.ip ClassFactor=\fIfact\fP
+[z]
+The indicated
+.i fact or
+is multiplied by the message class
+(determined by the Precedence: field in the user header
+and the
+.b P
+lines in the configuration file)
+and subtracted from the priority.
+Thus, messages with a higher Priority: will be favored.
+Defaults to 1800.
+.ip ColonOkInAddr
+[no short name]
+If set, colons are acceptable in e-mail addresses
+(e.g.,
+.q host:user ).
+If not set, colons indicate the beginning of a RFC 822 group construct
+(\c
+.q "groupname: member1, member2, ... memberN;" ).
+Doubled colons are always acceptable
+(\c
+.q nodename::user )
+and proper route-addr nesting is understood
+(\c
+.q <@relay:user@host> ).
+Furthermore, this option defaults on if the configuration version level
+is less than 6 (for back compatibility).
+However, it must be off for full compatibility with RFC 822.
+.ip ConnectionCacheSize=\fIN\fP
+[k]
+The maximum number of open connections that will be cached at a time.
+The default is one.
+This delays closing the current connection until
+either this invocation of
 .i sendmail
-will never rebuild the alias database
-unless explicitly requested
-using
-.b \-bi .
-.ip e\fIx\fP
-Dispose of errors using mode
-.i x .
-The values for
-.i x
-are:
-.(b
-p      Print error messages (default)
-q      No messages, just give exit status
-m      Mail back errors
-w      Write back errors (mail if user not logged in)
-e      Mail back errors and give zero exit stat always
+needs to connect to another host
+or it terminates.
+Setting it to zero defaults to the old behavior,
+that is, connections are closed immediately.
+Since this consumes file descriptors,
+the connection cache should be kept small:
+4 is probably a practical maximum.
+.ip ConnectionCacheTimeout=\fItimeout\fP
+[K]
+The maximum amount of time a cached connection will be permitted to idle
+without activity.
+If this time is exceeded,
+the connection is immediately closed.
+This value should be small (on the order of ten minutes).
+Before
+.i sendmail
+uses a cached connection,
+it always sends a RSET command
+to check the connection;
+if this fails, it reopens the connection.
+This keeps your end from failing if the other end times out.
+The point of this option is to be a good network neighbor
+and avoid using up excessive resources
+on the other end.
+The default is five minutes.
+.ip DaemonPortOptions=\fIoptions\fP
+[O]
+Set server SMTP options.
+The options are
+.i key=value
+pairs.
+Known keys are:
+.(b
+.ta 1i
+Port   Name/number of listening port (defaults to "smtp")
+Addr   Address mask (defaults INADDR_ANY)
+Family Address family (defaults to INET)
+Listen Size of listen queue (defaults to 10)
+SndBufSize     Size of TCP send buffer
+RcvBufSize     Size of TCP receive buffer
+.)b
+The
+.i Addr ess
+mask may be a numeric address in dot notation
+or a network name.
+.ip DefaultCharSet=\fIcharset\fP
+[no short name]
+When a message that has 8-bit characters but is not in MIME format
+is converted to MIME
+(see the EightBitMode option)
+a character set must be included in the Content-Type: header.
+This character set is normally set from the Charset= field
+of the mailer descriptor.
+If that is not set, the value of this option is used.
+If this option is not set, the value
+.q unknown-8bit
+is used.
+.ip DefaultUser=\fIuser:group\fP
+[u]
+Set the default userid for mailers to
+.i user:group .
+If
+.i group
+is omitted and 
+.i user
+is a user name
+(as opposed to a numeric user id)
+the default group listed in the /etc/passwd file for that user is used
+as the default group.
+Both
+.i user
+and
+.i group
+may be numeric.
+Mailers without the
+.i S
+flag in the mailer definition
+will run as this user.
+Defaults to 1:1.
+The value can also be given as a symbolic user name.\**
+.(f
+\**The old
+.b g
+option has been combined into the
+.b DefaultUser
+option.
+.)f
+.ip DeliveryMode=\fIx\fP
+[d]
+Deliver in mode
+.i x .
+Legal modes are:
+.(b
+.ta 4n
+i      Deliver interactively (synchronously)
+b      Deliver in background (asynchronously)
+q      Just queue the message (deliver during queue run)
+d      Defer delivery and all map lookups (deliver during queue run)
+.)b
+Defaults to ``b'' if no option is specified,
+``i'' if it is specified but given no argument
+(i.e., ``Od'' is equivalent to ``Odi'').
+The
+.b \-v
+command line flag sets this to
+.b i .
+.ip DialDelay=\fIsleeptime\fP
+[no short name]
+Dial-on-demand network connections can see timeouts
+if a connection is opened before the call is set up.
+If this is set to an interval and a connection times out
+on the first connection being attempted
+.i sendmail
+will sleep for this amount of time and try again.
+This should give your system time to establish the connection
+to your service provider.
+Units default to seconds, so
+.q DialDelay=5
+uses a five second delay.
+Defaults to zero
+(no retry).
+.ip DontExpandCnames
+[no short name]
+The standards say that all host addresses used in a mail message
+must be fully canonical.
+For example, if your host is named
+.q Cruft.Foo.ORG
+and also has an alias of
+.q FTP.Foo.ORG ,
+the former name must be used at all times.
+This is enforced during host name canonification
+($[ ... $] lookups).
+If this option is set, the protocols are ignored and the
+.q wrong
+thing is done.
+However, the IETF is moving toward changing this standard,
+so the behaviour may become acceptable.
+Please note that hosts downstream may still rewrite the address
+to be the true canonical name however.
+.ip DontInitGroups
+[no short name]
+If set,
+.i sendmail
+will avoid using the initgroups(3) call.
+If you are running NIS,
+this causes a sequential scan of the groups.byname map,
+which can cause your NIS server to be badly overloaded in a large domain.
+The cost of this is that the only group found for users
+will be their primary group (the one in the password file),
+which will make file access permissions somewhat more restrictive.
+Has no effect on systems that don't have group lists.
+.ip DontPruneRoutes
+[R]
+Normally,
+.i sendmail
+tries to eliminate any unnecessary explicit routes
+when sending an error message
+(as discussed in RFC 1123 \(sc 5.2.6).
+For example,
+when sending an error message to
+.(b
+<@known1,@known2,@known3:user@unknown>
+.)b
+.i sendmail
+will strip off the
+.q @known1,@known2
+in order to make the route as direct as possible.
+However, if the
+.b R
+option is set, this will be disabled,
+and the mail will be sent to the first address in the route,
+even if later addresses are known.
+This may be useful if you are caught behind a firewall.
+.ip EightBitMode=\fIaction\fP
+[8]
+Set handling of eight-bit data.
+There are two kinds of eight-bit data:
+that declared as such using the
+.b BODY=8BITMIME
+ESMTP declaration or the
+.b \-B8BITMIME
+command line flag,
+and undeclared 8-bit data, that is,
+input that just happens to be eight bits.
+There are three basic operations that can happen:
+undeclared 8-bit data can be automatically converted to 8BITMIME,
+undeclared 8-bit data can be passed as-is without conversion to MIME
+(``just send 8''),
+and declared 8-bit data can be converted to 7-bits
+for transmission to a non-8BITMIME mailer.
+The possible
+.i action s
+are:
+.(b
+.\"  r Reject undeclared 8-bit data;
+.\"    don't convert 8BITMIME\(->7BIT (``reject'')
+  s    Reject undeclared 8-bit data (``strict'')
+.\"    do convert 8BITMIME\(->7BIT (``strict'')
+.\"  c Convert undeclared 8-bit data to MIME;
+.\"    don't convert 8BITMIME\(->7BIT (``convert'')
+  m    Convert undeclared 8-bit data to MIME (``mime'')
+.\"    do convert 8BITMIME\(->7BIT (``mime'')
+.\"  j Pass undeclared 8-bit data;
+.\"    don't convert 8BITMIME\(->7BIT (``just send 8'')
+  p    Pass undeclared 8-bit data (``pass'')
+.\"    do convert 8BITMIME\(->7BIT (``pass'')
+.\"  a Adaptive algorithm: see below
 .)b
-.ip E\fIfile/message\fP
+.\"The adaptive algorithm is to accept 8-bit data,
+.\"converting it to 8BITMIME only if the receiver understands that,
+.\"otherwise just passing it as undeclared 8-bit data;
+.\"8BITMIME\(->7BIT conversions are done.
+In all cases properly declared 8BITMIME data will be converted to 7BIT
+as needed.
+.ip ErrorHeader=\fIfile-or-message\fP
+[E]
 Prepend error messages with the indicated message.
 If it begins with a slash,
 it is assumed to be the pathname of a file
@@ -3679,72 +4586,36 @@ in to end users.
 If the option is missing or null,
 or if it names a file which does not exist or which is not readable,
 no message is printed.
-.ip f
-Save
-Unix-style
-.q From
-lines at the front of headers.
-Normally they are assumed redundant
-and discarded.
-.ip F\fImode\fP
-The file mode for queue files.
-.ip g\fIn\fP
-Set the default group id
-for mailers to run in
-to
-.i n .
-Defaults to 1.
-The value can also be given as a symbolic group name.
-.ip G
-Allow fuzzy matching on the GECOS field.
-If this flag is set,
-and the usual user name lookups fail
-(that is, there is no alias with this name and a
-.i getpwnam
-fails),
-sequentially search the password file
-for a matching entry in the GECOS field.
-This also requires that MATCHGECOS
-be turned on during compilation.
-This option is not recommended.
-.ip h\fIN\fP
-The maximum hop count.
-Messages that have been processed more than
-.i N
-times are assumed to be in a loop and are rejected.
-Defaults to 25.
-.ip H\fIfile\fP
-Specify the help file
-for SMTP.
-.ip i
-Ignore dots in incoming messages.
-This is always disabled (that is, dots are always accepted)
-when reading SMTP mail.
-.ip I
-Insist that the BIND name server be running
-to resolve host names.
-If this is not set and the name server is not running,
-the
-.i /etc/hosts
-file will be considered complete.
-In general, you do want to set this option
-if your
-.i /etc/hosts
-file does not include all hosts known to you
-or if you are using the MX (mail forwarding) feature of the BIND name server.
-The name server will still be consulted
-even if this option is not set, but
-.i sendmail
-will feel free to resort to reading
-.i /etc/hosts
-if the name server is not available.
-Thus, you should
-.i never
-set this option if you do not run the name server.
-.ip j
-If set, send error messages in MIME format
-(see RFC1341 and RFC1344 for details).
-.ip J\fIpath\fP
+.ip ErrorMode=\fIx\fP
+[e]
+Dispose of errors using mode
+.i x .
+The values for
+.i x
+are:
+.(b
+p      Print error messages (default)
+q      No messages, just give exit status
+m      Mail back errors
+w      Write back errors (mail if user not logged in)
+e      Mail back errors and give zero exit stat always
+.)b
+.ip FallbackMXhost=\fIfallbackhost\fP
+[V]
+If specified, the
+.i fallbackhost
+acts like a very low priority MX
+on every host.
+This is intended to be used by sites with poor network connectivity.
+.ip ForkEachJob
+[Y]
+If set,
+deliver each job that is run from the queue in a separate process.
+Use this option if you are short of memory,
+since the default tends to consume considerable amounts of memory
+while the queue is being processed.
+.ip ForwardPath=\fIpath\fP
+[J]
 Set the path for searching for users' .forward files.
 The default is
 .q $z/.forward .
@@ -3761,57 +4632,131 @@ will search first in /var/forward/\c
 and then in
 .i ~username /.forward
 (but only if the first file does not exist).
-.ip k\fIN\fP
-The maximum number of open connections that will be cached at a time.
-The default is one.
-This delays closing the current connection until
-either this invocation of
-.i sendmail
-needs to connect to another host
-or it terminates.
-Setting it to zero defaults to the old behavior,
-that is, connections are closed immediately.
-.ip K\fItimeout\fP
-The maximum amount of time a cached connection will be permitted to idle
-without activity.
-If this time is exceeded,
-the connection is immediately closed.
-This value should be small (on the order of ten minutes).
-Before
-.i sendmail
-uses a cached connection,
-it always sends a NOOP (no operation) command
-to check the connection;
-if this fails, it reopens the connection.
-This keeps your end from failing if the other end times out.
-The point of this option is to be a good network neighbor
-and avoid using up excessive resources
-on the other end.
-The default is five minutes.
-.ip l
-If there is an
-.q Errors-To:
-header, send error messages to the addresses listed there.
-They normally go to the envelope sender.
-Use of this option causes
-.i sendmail
-to violate RFC 1123.
-.ip L\fIn\fP
+.ip HelpFile=\fIfile\fP
+[H]
+Specify the help file
+for SMTP.
+.ip HoldExpensive
+[c]
+If an outgoing mailer is marked as being expensive,
+don't connect immediately.
+This requires that queueing be compiled in,
+since it will depend on a queue run process to
+actually send the mail.
+.ip IgnoreDots
+[i]
+Ignore dots in incoming messages.
+This is always disabled (that is, dots are always accepted)
+when reading SMTP mail.
+.ip LogLevel=\fIn\fP
+[L]
 Set the default log level to
 .i n .
 Defaults to 9.
-.ip m
-Send to me too,
-even if I am in an alias expansion.
 .ip M\fIx\|value\fP
+[no long version]
 Set the macro
 .i x
 to
 .i value .
 This is intended only for use from the command line.
-.ip n
-Validate the RHS of aliases when rebuilding the alias database.
-.ip o
+The
+.b \-M
+flag is preferred.
+.ip MatchGECOS
+[G]
+Allow fuzzy matching on the GECOS field.
+If this flag is set,
+and the usual user name lookups fail
+(that is, there is no alias with this name and a
+.i getpwnam
+fails),
+sequentially search the password file
+for a matching entry in the GECOS field.
+This also requires that MATCHGECOS
+be turned on during compilation.
+This option is not recommended.
+.ip MaxHopCount=\fIN\fP
+[h]
+The maximum hop count.
+Messages that have been processed more than
+.i N
+times are assumed to be in a loop and are rejected.
+Defaults to 25.
+.ip MaxHostStatAge=\fIage\fP
+[no short name]
+Not yet implemented.
+This option specifies how long host status information will be retained.
+For example, if a host is found to be down,
+connections to that host will not be retried for this interval.
+The units default to minutes.
+.ip MaxQueueRunSize=\fIN\fP
+[no short name]
+The maximum number of jobs that will be processed
+in a single queue run.
+If not set, there is no limit on the size.
+If you have very large queues or a very short queue run interval
+this could be unstable.
+However, since the first
+.i N
+jobs in queue directory order are run (rather than the
+.i N
+highest priority jobs)
+this should be set as high as possible to avoid
+.q losing
+jobs that happen to fall late in the queue directory.
+.ip MeToo
+[m]
+Send to me too,
+even if I am in an alias expansion.
+.ip MaxMessageSize=\fIN\fP
+[no short name]
+Specify the maximum message size
+to be advertised in the ESMTP EHLO response.
+Messages larger than this will be rejected.
+.ip MinFreeBlocks=\fIN\fP
+[b]
+Insist on at least
+.i N
+blocks free on the filesystem that holds the queue files
+before accepting email via SMTP.
+If there is insufficient space
+.i sendmail
+gives a 452 response
+to the MAIL command.
+This invites the sender to try again later.
+.ip MinQueueAge=\fPage\fP
+[no short name]
+Don't process any queued jobs
+that have been in the queue less than the indicated time interval.
+This is intended to allow you to get responsiveness
+by processing the queue fairly frequently
+without thrashing your system by trying jobs too often.
+The default units are minutes.
+.ip NoRecipientAction
+[no short name]
+The action to take when you receive a message that has no valid
+recipient headers (To:, Cc:, Bcc:).
+It can be
+.b None
+to pass the message on unmodified,
+which violates the protocol,
+.b Add-To
+to add a To: header with any recipients it can find in the envelope
+(which might expose Bcc: recipients),
+.b Add-Apparently-To
+to add an Apparently-To: header
+(this is only for back-compatibility
+and is officially deprecated),
+.b Add-To-Undisclosed
+to add a header
+.q "To: undisclosed-recipients:;"
+to make the header legal without disclosing anything,
+or
+.b Add-Bcc
+to add an empty Bcc: header.
+.ip OldStyleHeaders
+[o]
 Assume that the headers may be in old format,
 i.e.,
 spaces delimit names.
@@ -3823,24 +4768,38 @@ it will be assumed that commas already exist.
 If this flag is not on,
 only commas delimit names.
 Headers are always output with commas between the names.
-.ip O\fIoptions\fP
-Set server SMTP options.
-The options are
-.i key=value
-pairs.
-Known keys are:
-.(b
-.ta 1i
-Port   Name/number of listening port (defaults to "smtp")
-Addr   Address mask (defaults INADDR_ANY)
-Family Address family (defaults to INET)
-Listen Size of listen queue (defaults to 10)
-.)b
-The
-.i Addr ess
-mask may be a numeric address in dot notation
-or a network name.
-.ip p\fI\|opt,opt,...\fP
+Defaults to off.
+.ip OperatorChars=\fIcharlist\fP
+[$o macro]
+The list of characters that are considered to be
+.q operators ,
+that is, characters that delimit tokens.
+All operator characters are tokens by themselves;
+sequences of non-operator characters are also tokens.
+White space characters separate tokens
+but are not tokens themselves \(em for example,
+.q AAA.BBB
+has three tokens, but
+.q "AAA BBB"
+has two.
+If not set, OperatorChars defaults to
+.q \&.\|:\|@\|[\|] ;
+additionally, the characters
+.q (\|)\|<\|>\|,\|;
+are always operators.
+.ip PostmasterCopy=\fIpostmaster\fP
+[P]
+If set,
+copies of error messages will be sent to the named
+.i postmaster .
+Only the header of the failed message is sent.
+Since most errors are user problems,
+this is probably not a good idea on large sites,
+and arguably contains all sorts of privacy violations,
+but it seems to be popular with certain operating systems vendors.
+Defaults to no postmaster copies.
+.ip PrivacyOptions=\fI\|opt,opt,...\fP
+[p]
 Set the privacy
 .i opt ions.
 ``Privacy'' is really a misnomer;
@@ -3859,7 +4818,7 @@ needvrfyhelo      Insist on HELO or EHLO command before VRFY
 novrfy Disallow VRFY entirely
 restrictmailq  Restrict mailq command
 restrictqrun   Restrict \-q command line flag
-noreceipts     Ignore Return-Receipt-To: header
+noreceipts     Don't return success DSNs
 goaway Disallow essentially all SMTP status queries
 authwarnings   Put X-Authentication-Warning: headers in messages
 .)b
@@ -3878,16 +4837,13 @@ can run the queue.
 Authentication Warnings add warnings about various conditions
 that may indicate attempts to spoof the mail system,
 such as using an non-standard queue directory.
-.ip P\fIpostmaster\fP
-If set,
-copies of error messages will be sent to the named
-.i postmaster .
-Only the header of the failed message is sent.
-Since most errors are user problems,
-this is probably not a good idea on large sites,
-and arguably contains all sorts of privacy violations,
-but it seems to be popular with certain operating systems vendors.
-.ip q\fIfactor\fP
+.ip QueueDirectory=\fIdir\fP
+[Q]
+Use the named
+.i dir
+as the queue directory.
+.ip QueueFactor=\fIfactor\fP
+[q]
 Use
 .i factor
 as the multiplier in the map function
@@ -3895,24 +4851,86 @@ to decide when to just queue up jobs rather than run them.
 This value is divided by the difference between the current load average
 and the load average limit
 (\c
-.b x
-flag)
+.b QueueLA
+option)
 to determine the maximum message priority
 that will be sent.
 Defaults to 600000.
-.ip Q\fIdir\fP
-Use the named
-.i dir
-as the queue directory.
-.ip r\|\fItimeouts\fP
-Timeout reads after
-.i time
-interval.
-The
-.i timeouts
-argument is a list of
-.i keyword=value
-pairs.
+.ip QueueLA=\fILA\fP
+[x]
+When the system load average exceeds
+.i LA ,
+just queue messages
+(i.e., don't try to send them).
+Defaults to 8.
+.ip QueueSortOrder=\fIalgorithm\fP
+[no short name]
+Sets the
+.i algorithm
+used for sorting the queue.
+Only the first character of the value is used.
+Legal values are
+.q host
+(to order by the name of the first host name of the first recipient)
+and
+.q priority
+(to order strictly by message priority).
+Host ordering makes better use of the connection cache,
+but may tend to process low priority messages
+that go to a single host
+over high priority messages that go to several hosts;
+it probably shouldn't be used on slow network links.
+Priority ordering is the default.
+.ip ResolverOptions=\fIoptions\fP
+[I]
+Set resolver options.
+Values can be set using
+.b + \c
+.i flag
+and cleared using
+.b \- \c
+.i flag ;
+the
+.i flag s
+can be
+.q debug ,
+.q aaonly ,
+.q usevc ,
+.q primary ,
+.q igntc ,
+.q recurse ,
+.q defnames ,
+.q stayopen ,
+or
+.q dnsrch .
+The string
+.q HasWildcardMX
+(without a
+.b +
+or
+.b \- )
+can be specified to turn off matching against MX records
+when doing name canonifications.
+.b N.B.
+Prior to 8.7,
+this option indicated that the name server be responding
+in order to accept addresses.
+This has been replaced by checking to see
+if the
+.q dns
+method is listed in the service switch entry for the
+.q hosts
+service.
+.ip SmtpGreetingMessage=\fImessage\fP
+[$e macro]
+The message printed when the SMTP server starts up.
+Defaults to
+.q "$j Sendmail $v ready at $b".
+.ip Timeout.\fItype\fP=\|\fItimeout\fP
+[r; subsumes old T option as well]
+Set timeout values.
+The actual timeout is indicated by the
+.i type .
 The recognized timeouts and their default values, and their
 minimum values specified in RFC 1123 section 5.3.2 are:
 .(b
@@ -3927,37 +4945,128 @@ datafinal      reply to final ``.'' in data [1h, 10m]
 rset   reply to RSET command [5m, none]
 quit   reply to QUIT command [2m, none]
 misc   reply to NOOP and VERB commands [2m, none]
-command        command read [1h, 5m]
 ident  IDENT protocol timeout [30s, none]
+fileopen\(dg   timeout on opening .forward and :include: files [60s, none]
+command\(dg    command read [1h, 5m]
+queuereturn\(dg        how long until a message is returned [5d, 5d]
+queuewarn\(dg  how long until a warning is sent [none, none]
 .)b
-All but
-.q command
+All but those marked with a dagger (\(dg)
 apply to client SMTP.
-For back compatibility,
-a timeout with no ``keyword='' part
-will set all of the longer values.
-.ip R
-Normally,
+If the message is submitted using the
+.sm NOTIFY
+.sm SMTP
+extension,
+warning messages will only be sent if
+.sm NOTIFY=DELAY
+is specified.
+The queuereturn and queuewarn timeouts
+can be further qualified with a tag based on the Precedence: field
+in the message;
+they must be one of
+.q urgent
+(indicating a positive non-zero precedence)
+.q normal
+(indicating a zero precedence), or
+.q non-urgent
+(indicating negative precedences).
+For example, setting
+.q Timeout.queuewarn.urgent=1h
+sets the warning timeout for urgent messages only
+to one hour.
+The default if no precedence is indicated
+is to set the timeout for all precedences.
+.ip RecipientFactor=\fIfact\fP
+[y]
+The indicated
+.i fact or
+is added to the priority (thus
+.i lowering
+the priority of the job)
+for each recipient,
+i.e., this value penalizes jobs with large numbers of recipients.
+Defaults to 30000.
+.ip RefuseLA=\fILA\fP
+[X]
+When the system load average exceeds
+.i LA ,
+refuse incoming SMTP connections.
+Defaults to 12.
+.ip RetryFactor=\fIfact\fP
+[Z]
+The
+.i fact or
+is added to the priority
+every time a job is processed.
+Thus,
+each time a job is processed,
+its priority will be decreased by the indicated value.
+In most environments this should be positive,
+since hosts that are down are all too often down for a long time.
+Defaults to 90000.
+.ip SaveFromLine
+[f]
+Save
+Unix-style
+.q From
+lines at the front of headers.
+Normally they are assumed redundant
+and discarded.
+.ip SendMIMEErrors
+[j]
+If set, send error messages in MIME format
+(see RFC1521 and RFC1344 for details).
+.ip ServiceSwitchFile=\fIfilename\fP
+[no short name]
+If your host operating system has a service switch abstraction
+(e.g., /etc/nsswitch.conf on Solaris
+or /etc/svc.conf on Ultrix and DEC OSF/1)
+that service will be consulted and this option is ignored.
+Otherwise, this is the name of a file
+that provides the list of methods used to implement particular services.
+The syntax is a series of lines,
+each of which is a sequence of words.
+The first word is the service name,
+and following words are service types.
+The services that
 .i sendmail
-tries to eliminate any unnecessary explicit routes
-when sending an error message
-(as discussed in RFC 1123 \(sc 5.2.6).
-For example,
-when sending an error message to
+consults directly are
+.q aliases
+and
+.q hosts.
+Service types can be
+.q dns ,
+.q nis ,
+.q nisplus ,
+or
+.q files
+(with the caveat that the appropriate support
+must be compiled in
+before the service can be referenced).
+If ServiceSwitchFile is not specified, it defaults to /etc/service.switch.
+If that file does not exist, the default switch is:
 .(b
-<@known1,@known2,@unknown:user@known3>
+aliases        files
+hosts  dns nis files
 .)b
-.i sendmail
-will strip off the
-.q @known1
-in order to make the route as direct as possible.
-However, if the
-.b R
-option is set, this will be disabled,
-and the mail will be sent to the first address in the route,
-even if later addresses are known.
-This may be useful if you are caught behind a firewall.
-.ip s
+The default file is
+.q /etc/service.switch .
+.ip SevenBitInput
+[7]
+Strip input to seven bits for compatibility with old systems.
+This shouldn't be necessary.
+.ip StatusFile=\fIfile\fP
+[S]
+Log summary statistics in the named
+.i file .
+If not set,
+no summary statistics are saved.
+This file does not grow in size.
+It can be printed using the
+.i mailstats (8)
+program.
+.ip SuperSafe
+[s]
 Be super-safe when running things,
 i.e.,
 always instantiate the queue file,
@@ -3966,10 +5075,16 @@ even if you are going to attempt immediate delivery.
 always instantiates the queue file
 before returning control the client
 under any circumstances.
-.ip S\fIfile\fP
-Log statistics in the named
-.i file .
-.ip t\fItzinfo\fP
+This should really
+.i always
+be set.
+.ip TempFileMode=\fImode\fP
+[F]
+The file mode for queue files.
+It is interpreted in octal by default.
+Defaults to 0600.
+.ip TimeZoneSpec=\fItzinfo\fP
+[t]
 Set the local time zone info to
 .i tzinfo
 \*- for example,
@@ -3978,61 +5093,14 @@ Actually, if this is not set,
 the TZ environment variable is cleared (so the system default is used);
 if set but null, the user's TZ variable is used,
 and if set and non-null the TZ variable is set to this value.
-.ip T\fIrtime/wtime\fP
-Set the queue timeout to
-.i rtime .
-After this interval,
-messages that have not been successfully sent
-will be returned to the sender.
-Defaults to five days.
-The optional
-.i wtime
-is the time after which a warning message is sent.
-If it is missing or zero
-then no warning messages are sent.
-.ip u\fIn\fP
-Set the default userid for mailers to
-.i n .
-Mailers without the
-.i S
-flag in the mailer definition
-will run as this user.
-Defaults to 1.
-The value can also be given as a symbolic user name.
-.ip U\fIudbspec\fP
-The user database specification.
-.ip v
-Run in verbose mode.
-If this is set,
-.i sendmail
-adjusts options
-.b c
-(don't connect to expensive mailers)
-and
-.b d
-(delivery mode)
-so that all mail is delivered completely
-in a single job
-so that you can see the entire delivery process.
-Option
-.b v
-should
-.i never
-be set in the configuration file;
-it is intended for command line use only.
-.ip V\fIfallbackhost\fP
-If specified, the
-.i fallbackhost
-acts like a very low priority MX
-on every host.
-This is intended to be used by sites with poor network connectivity.
-.ip w
-If you are the
+.ip TryNullMXList
+[w]
+If this system is the
 .q best
 (that is, lowest preference)
 MX for a given host,
-you should normally detect this situation
-and treat that condition specially,
+its configuration rules should normally detect this situation
+and treat that condition specially
 by forwarding the mail to a UUCP feed,
 treating it as local,
 or whatever.
@@ -4049,69 +5117,77 @@ or
 .q "message timed out"
 instead of something more meaningful.
 This option is disrecommended.
-.ip x\fILA\fP
-When the system load average exceeds
-.i LA ,
-just queue messages
-(i.e., don't try to send them).
-Defaults to 8.
-.ip X\fILA\fP
-When the system load average exceeds
-.i LA ,
-refuse incoming SMTP connections.
-Defaults to 12.
-.ip y\fIfact\fP
-The indicated
-.i fact or
-is added to the priority (thus
-.i lowering
-the priority of the job)
-for each recipient,
-i.e., this value penalizes jobs with large numbers of recipients.
-Defaults to 30000.
-.ip Y
-If set,
-deliver each job that is run from the queue in a separate process.
-Use this option if you are short of memory,
-since the default tends to consume considerable amounts of memory
-while the queue is being processed.
-.ip z\fIfact\fP
-The indicated
-.i fact or
-is multiplied by the message class
-(determined by the Precedence: field in the user header
-and the
-.b P
-lines in the configuration file)
-and subtracted from the priority.
-Thus, messages with a higher Priority: will be favored.
-Defaults to 1800.
-.ip Z\fIfact\fP
-The
-.i fact or
-is added to the priority
-every time a job is processed.
-Thus,
-each time a job is processed,
-its priority will be decreased by the indicated value.
-In most environments this should be positive,
-since hosts that are down are all too often down for a long time.
-Defaults to 90000.
-.ip 7
-Strip input to seven bits for compatibility with old systems.
-This shouldn't be necessary.
+.ip UnixFromLine=\fIfromline\fP
+[$l macro]
+Defines the format used when
+.i sendmail
+must add a UNIX-style From_ line
+(that is, a line beginning
+.q From<space>user ).
+Defaults to
+.q "From $g  $d" .
+Don't change this unless your system uses a different UNIX mailbox format
+(very unlikely).
+.ip UseErrorsTo
+[l]
+If there is an
+.q Errors-To:
+header, send error messages to the addresses listed there.
+They normally go to the envelope sender.
+Use of this option causes
+.i sendmail
+to violate RFC 1123.
+This option is disrecommended and deprecated.
+.ip UserDatabaseSpec=\fIudbspec\fP
+[U]
+The user database specification.
+.ip Verbose
+[v]
+Run in verbose mode.
+If this is set,
+.i sendmail
+adjusts options
+.b HoldExpensive
+(old
+.b c )
+and
+.b DeliveryMode
+(old
+.b d )
+so that all mail is delivered completely
+in a single job
+so that you can see the entire delivery process.
+Option
+.b Verbose
+should
+.i never
+be set in the configuration file;
+it is intended for command line use only.
 .lp
 All options can be specified on the command line using the
-\-o flag,
+\-O or \-o flag,
 but most will cause
 .i sendmail
 to relinquish its setuid permissions.
 The options that will not cause this are
-b, d, e, i, L, m, o, p, r, s, v, C, and 7.
+MinFreeBlocks [b],
+DeliveryMode [d],
+ErrorMode [e],
+IgnoreDots [i],
+LogLevel [L],
+MeToo [m],
+OldStyleHeaders [o],
+PrivacyOptions [p],
+Timeouts [r],
+SuperSafe [s],
+Verbose [v],
+CheckpointInterval [C],
+and
+SevenBitInput [7].
 Also, M (define macro) when defining the r or s macros
 is also considered
 .q safe .
-.sh 3 "P \*- precedence definitions"
+.sh 2 "P \*- Precedence Definitions"
 .pp
 Values for the
 .q "Precedence:"
@@ -4157,7 +5233,7 @@ didn't recognize this name, giving it a default precedence of zero.
 This allows list maintainers to see error returns
 on both old and new versions of
 .i sendmail .
-.sh 3 "V \*- configuration version level"
+.sh 2 "V \*- Configuration Version Level"
 .pp
 To provide compatibility with old configuration files,
 the
@@ -4178,8 +5254,8 @@ on the files.
 For example,
 as of this writing
 version 8 config files
-(specifically, 8.6)
-used version level 5 configurations.
+(specifically, 8.7)
+used version level 6 configurations.
 .pp
 .q Old
 configuration files are defined as version level one.
@@ -4237,6 +5313,22 @@ change the default definition of
 .b $w
 to be just the first component of the hostname.
 .pp
+Version level six configuration files
+change many of the local processing options
+(such as aliasing and matching the beginning of the address for
+`|' characters)
+to be mailer flags;
+this allows fine-grained control over the special local processing.
+Level six configuration files may also use long option names.
+The
+.b ColonOkInAddr
+option (to allow colons in the local-part of addresses)
+defaults
+.b on
+for lower numbered configuration files;
+the configuration file requires some additional intelligence
+to properly handle the RFC 822 group construct.
+.pp
 The
 .b V
 line may have an optional
@@ -4250,8 +5342,15 @@ to the list of recognized vendors by editing the routine
 .i setvendor
 in
 .i conf.c .
+Please send e-mail to sendmail@CS.Berkeley.EDU
+to register your vendor dialect.
 .)f
-.sh 3 "K \*- key file declaration"
+You may use
+.q /Berkeley
+to emphasize that this configuration file
+uses the Berkeley dialect of
+.i sendmail .
+.sh 2 "K \*- Key File Declaration"
 .pp
 Special maps can be defined using the line:
 .(b
@@ -4281,7 +5380,7 @@ or
 .i default
 portion may be omitted.
 The
-.i arguments
+.i "$@ arguments"
 may appear more than once.
 The indicated
 .i key
@@ -4309,7 +5408,7 @@ is always the database key.
 For example, the rule
 .(b
 .ta 1.5i
-R$- ! $+       $: $(uucp $1 $@ $2 $: %1 @ %0 . UUCP $)
+R$\- ! $+      $: $(uucp $1 $@ $2 $: %1 @ %0 . UUCP $)
 .)b
 Looks up the UUCP name in a (user defined) UUCP map;
 if not found it turns it into
@@ -4334,24 +5433,156 @@ is equivalent to:
 $[\fIhostname\fP$]
 .)b
 .pp
-There are four predefined database lookup classes:
-.q dbm ,
-.q btree ,
-.q hash ,
+There are many defined classes.
+.ip dbm
+Database lookups using the ndbm(3) library.
+.i Sendmail
+must be compiled with
+.b NDBM
+defined.
+.ip btree
+Database lookups using the btree interface to the Berkeley db(3) library.
+.i Sendmail
+must be compiled with
+.b NEWDB
+defined.
+.ip hash
+Database lookups using the hash interface to the Berkeley db(3) library.
+.i Sendmail
+must be compiled with
+.b NEWDB
+defined.
+.ip nis
+NIS lookups.
+.i Sendmail
+must be compiled with
+.b NIS
+defined.
+.ip nisplus
+NIS+ lookups.
+.i Sendmail
+must be compiled with
+.b NISPLUS
+defined.
+The argument is the name of the table to use for lookups,
+and the
+.b \-k
+and
+.b \-v
+flags may be used to set the key and value columns respectively.
+.ip hesiod
+Hesiod lookups.
+.i Sendmail
+must be compiled with
+.b HESIOD
+defined.
+.ip netinfo
+NeXT NetInfo lookups.
+.i Sendmail
+must be compiled with
+.b NETINFO
+defined.
+.ip text
+Text file lookups.
+The format of the text file is defined by the
+.b \-k
+(key field number),
+.b \-v
+(value field number),
+and
+.b \-z
+(field delimiter)
+flags.
+.ip stab
+Internal symbol table lookups.
+Used internally for aliasing.
+.ip implicit
+Really should be called
+.q alias
+\(em this is used to get the default lookups
+for alias files,
+and is the default if no class is specified for alias files.
+.ip user
+Looks up users using
+.i getpwnam (3).
+The
+.b \-v
+flag can be used to specify the name of the field to return
+(although this is normally used only to check the existence
+of a user).
+.ip host
+Canonifies host domain names.
+Given a host name it calls the name server
+to find the canonical name for that host.
+.ip sequence
+The arguments on the `K' line are a list of maps;
+the resulting map searches the argument maps in order
+until it finds a match for the indicated key.
+For example, if the key definition is:
+.(b
+Kmap1 ...
+Kmap2 ...
+Kseqmap sequence map1 map2
+.)b
+then a lookup against
+.q seqmap
+first does a lookup in map1.
+If that is found, it returns immediately.
+Otherwise, the same key is used for map2.
+.ip switch
+Much like the
+.q sequence
+map except that the order of maps is determined by the service switch.
+The argument is the name of the service to be looked up;
+the values from the service switch are appended to the map name
+to create new map names.
+For example, consider the key definition:
+.(b
+Kali switch aliases
+.)b
+together with the service switch entry:
+.(b
+aliases        nis files
+.)b
+This causes a query against the map
+.q ali
+to search maps named
+.q ali.nis
 and
-.q nis .
-The first requires that
-.i sendmail
-be compiled with the
-.b ndbm
-library;
-the second two require the
-.b db
-library,
-and the third requires that
-.i sendmail
-be compiled with NIS support.
-All four accept as arguments the same optional flags
+.q ali.files
+in that order.
+.ip dequote
+Strip double quotes (") from a name.
+It does not strip backslashes,
+and will not strip quotes if the resulting string
+would contain unscannable syntax
+(that is, basic errors like unbalanced angle brackets;
+more sophisticated errors such as unknown hosts are not checked).
+The intent is for use when trying to accept mail from systems such as
+DECnet
+that routinely quote odd syntax such as
+.(b
+"49ers::ubell"
+.)b
+A typical usage is probably something like:
+.(b
+Kdequote dequote
+
+\&...
+
+R$\-   $: $(dequote $1 $)
+R$\- $+        $: $>3 $1 $2
+.)b
+Care must be taken to prevent unexpected results;
+for example,
+.(b
+"|someprogram < input > output"
+.)b
+will have quotes stripped,
+but the result is probably not what you had in mind.
+Fortunately these cases are rare.
+.pp
+Most of these accept as arguments the same optional flags
 and a filename
 (or a mapname for NIS;
 the filename is the root of the database path,
@@ -4367,14 +5598,7 @@ no error is produced,
 and
 .i sendmail
 will behave as if the map existed but was empty.
-.ip "\-N"
-Normally when maps are written,
-the trailing null byte is not included as part of the key.
-If this flag is indicated it will be included.
-During lookups, only the null-byte-included form will be searched.
-See also
-.b \-O.
-.ip "\-O"
+.ip "\-N, \-O"
 If neither
 .b \-N
 or
@@ -4386,9 +5610,14 @@ on the end of keys.
 It starts by trying both;
 if it finds any key with a null byte it never tries again without a null byte
 and vice versa.
-If this flag is specified,
-it never tries with a null byte;
-this can speed matches but is never necessary.
+If
+.b \-N
+is specified it never tries without a null byte and
+if
+.b \-O
+is specified it never tries with a null byte.
+Setting one of
+these can speed matches but are never necessary.
 If both
 .b \-N
 and
@@ -4416,6 +5645,25 @@ this flag prevents the map from substituting the value.
 However,
 The \-a argument is still appended on a match,
 and the default is still taken if the match fails.
+.ip "\-k\fIkeycol\fP"
+The key column name (for NIS+) or number
+(for text lookups).
+.ip "\-v\fIvalcol\fP"
+The value column name (for NIS+) or number
+(for text lookups).
+.ip "\-z\fIdelim\fP"
+The column delimiter (for text lookups).
+It can be a single character or one of the special strings
+.q \|\en
+or
+.q \|\et
+to indicate newline or tab respectively.
+If omitted entirely,
+the column separator is any sequence of whitespace.
+.ip "\-s\fIspacesub\fP
+For the dequote map only,
+the character to use to replace space characters
+after a successful dequote.
 .pp
 The
 .i dbm
@@ -4428,729 +5676,52 @@ the two
 .i db -based
 maps append
 .q \&.db .
-For example, the map specification
-.(b
-Kuucp dbm \-o \-N /usr/lib/uucpmap
-.)b
-specifies an optional map named
-.q uucp
-of class
-.q dbm ;
-it always has null bytes at the end of every string,
-and the data is located in
-/usr/lib/uucpmap.{dir,pag}.
-.pp
-The program
-.i makemap (8)
-can be used to build any of the three database-oriented maps.
-It takes the following flags:
-.ip \-f
-Fold upper to lower case in the map.
-.ip \-N
-Include null bytes in keys.
-.ip \-o
-Append to an existing (old) file.
-.ip \-r
-Allow replacement of existing keys;
-normally, re-inserting an existing key is an error.
-.ip \-v
-Print what is happening.
-.lp
-The
-.i sendmail
-daemon does not have to be restarted to read the new maps
-as long as you change them in place;
-file locking is used so that the maps won't be read
-while they are being updated.\**
-.(f
-\**That is, don't create new maps and then use
-.i mv (1)
-to move them into place.
-I consider this a shortfall (a.k.a. bug) in
-.i sendmail
-which should be fixed in a future release.
-.)f
-.pp
-There are also two builtin maps that are,
-strictly speaking,
-not database lookups.
-.pp
-The
-.q host
-map does host domain canonification;
-given a host name it calls the name server
-to find the canonical name for that host.
-.pp
-The
-.q dequote
-map strips double quotes (") from a name.
-It does not strip backslashes.
-It will not strip quotes if the resulting string
-would contain unscannable syntax
-(that is, basic errors like unbalanced angle brackets;
-more sophisticated errors such as unknown hosts are not checked).
-The intent is for use when trying to accept mail from systems such as
-DECnet
-that routinely quote odd syntax such as
-.(b
-"49ers::ubell"
-.)b
-A typical usage is probably something like:
-.(b
-Kdequote dequote
-
-\&...
-
-R$\-   $: $(dequote $1 $)
-R$\- $+        $: $>3 $1 $2
-.)b
-Care must be taken to prevent unexpected results;
-for example,
-.(b
-"|someprogram < input > output"
-.)b
-will have quotes stripped,
-but the result is probably not what you had in mind.
-Fortunately these cases are rare.
-.pp
-New classes can be added in the routine
-.b setupmaps
-in file
-.b conf.c .
-.sh 2 "Building a Configuration File From Scratch"
-.pp
-Building a configuration table from scratch is an extremely difficult job.
-Fortunately,
-it is almost never necessary to do so;
-nearly every situation that may come up
-may be resolved by changing an existing table.
-In any case,
-it is critical that you understand what it is that you are trying to do
-and come up with a philosophy for the configuration table.
-This section is intended to explain what the real purpose
-of a configuration table is
-and to give you some ideas
-for what your philosophy might be.
-.pp
-.b "Do not even consider"
-writing your own configuration file
-without carefully studying
-RFC 821, 822, and 1123.
-You should also read RFC 976
-if you are doing UUCP exchange.
-.sh 3 "What you are trying to do"
-.pp
-The configuration table has three major purposes.
-The first and simplest
-is to set up the environment for
-.i sendmail .
-This involves setting the options,
-defining a few critical macros,
-etc.
-Since these are described in other places,
-we will not go into more detail here.
-.pp
-The second purpose is to rewrite addresses in the message.
-This should typically be done in two phases.
-The first phase maps addresses in any format
-into a canonical form.
-This should be done in ruleset three.
-The second phase maps this canonical form
-into the syntax appropriate for the receiving mailer.
-.i Sendmail
-does this in three subphases.
-Rulesets one and two
-are applied to all sender and recipient addresses respectively.
-After this,
-you may specify per-mailer rulesets
-for both sender and recipient addresses;
-this allows mailer-specific customization.
-Finally,
-ruleset four is applied to do any default conversion
-to external form.
-.pp
-The third purpose
-is to map addresses into the actual set of instructions
-necessary to get the message delivered.
-Ruleset zero must resolve to the internal form,
-which is in turn used as a pointer to a mailer descriptor.
-The mailer descriptor describes the interface requirements
-of the mailer.
-.sh 3 "Philosophy"
-.pp
-The particular philosophy you choose will depend heavily
-on the size and structure of your organization.
-I will present a few possible philosophies here.
-There are as many philosophies as there are config designers;
-feel free to develop your own.
-.pp
-One general point applies to all of these philosophies:
-it is almost always a mistake
-to try to do full host route resolution.
-For example,
-if you are on a UUCP-only site
-and you are trying to get names of the form
-.q user@host
-to the Internet,
-it does not pay to route them to
-.q xyzvax!decvax!ucbvax!c70!user@host
-since you then depend on several links not under your control,
-some of which are likely to misparse it anyway.
-The best approach to this problem
-is to simply forward the message for
-.q user@host
-to
-.q xyzvax
-and let xyzvax
-worry about it from there.
-In summary,
-just get the message closer to the destination,
-rather than determining the full path.
-.sh 4 "Large site, many hosts \*- minimum information"
-.pp
-Berkeley is an example of a large site,
-i.e., more than two or three hosts
-and multiple mail connections.
-We have decided that the only reasonable philosophy
-in our environment
-is to designate one host as the guru for our site.
-It must be able to resolve any piece of mail it receives.
-The other sites should have the minimum amount of information
-they can get away with.
-In addition,
-any information they do have
-should be hints rather than solid information.
-.pp
-For example,
-a typical site on our local ether network is
-.q monet
-(actually
-.q monet.CS.Berkeley.EDU ).
-When monet receives mail for delivery,
-it checks whether it knows
-that the destination host is directly reachable;
-if so, mail is sent to that host.
-If it receives mail for any unknown host,
-it just passes it directly to
-.q ucbvax.CS.Berkeley.EDU ,
-our master host.
-Ucbvax may determine that the host name is illegal
-and reject the message,
-or may be able to do delivery.
-However, it is important to note that when a new mail connection is added,
-the only host that
-.i must
-have its tables updated
-is ucbvax;
-the others
-.i may
-be updated if convenient,
-but this is not critical.
-.pp
-This picture is slightly muddied
-due to network connections that are not actually located
-on ucbvax.
-For example,
-some UUCP connections are currently on
-.q ucbarpa.
-However,
-monet
-.i "does not"
-know about this;
-the information is hidden totally between ucbvax and ucbarpa.
-Mail going from monet to a UUCP host
-is transferred via the ethernet
-from monet to ucbvax,
-then via the ethernet from ucbvax to ucbarpa,
-and then is submitted to UUCP.
-Although this involves some extra hops,
-we feel this is an acceptable tradeoff.
-.pp
-An interesting point is that it would be possible
-to update monet
-to send appropriate UUCP mail directly to ucbarpa
-if the load got too high;
-if monet failed to note a host as connected to ucbarpa
-it would go via ucbvax as before,
-and if monet incorrectly sent a message to ucbarpa
-it would still be sent by ucbarpa
-to ucbvax as before.
-The only problem that can occur is loops,
-for example,
-if ucbarpa thought that ucbvax had the UUCP connection
-and vice versa.
-For this reason,
-updates should
-.i always
-happen to the master host first.
-.pp
-This philosophy results as much from the need
-to have a single source for the configuration files
-(typically built using
-.i m4 \|(1)
-or some similar tool)
-as any logical need.
-Maintaining more than three separate tables by hand
-is essentially an impossible job.
-.sh 4 "Small site \*- complete information"
-.pp
-A small site
-(two or three hosts and few external connections)
-may find it more reasonable to have complete information
-at each host.
-This would require that each host
-know exactly where each network connection is,
-possibly including the names of each host on that network.
-As long as the site remains small
-and the configuration remains relatively static,
-the update problem will probably not be too great.
-.sh 4 "Single host"
-.pp
-This is in some sense the trivial case.
-The only major issue is trying to insure that you don't
-have to know too much about your environment.
-For example,
-if you have a UUCP connection
-you might find it useful to know about the names of hosts
-connected directly to you,
-but this is really not necessary
-since this may be determined from the syntax.
-.sh 4 "A completely different philosophy"
-.pp
-This is adapted from Bruce Lilly.
-Any errors in interpretation are mine.
-.pp
-Do minimal changes in ruleset 3:
-fix some common but unambiguous errors (e.g. trailing dot on domains) and
-hide bang paths foo!bar into bar@foo.UUCP.
-The resulting "canonical" form is any valid RFC822/RFC1123/RFC976 address.
-.pp
-Ruleset 0 does the bulk of the work.
-It removes the trailing "@.UUCP" that hides bang paths,
-strips anything not needed to resolve,
-e.g. the phrase from phrase <route-addr> and from named groups,
-rejects unparseable addresses using $#error,
-and finally
-resolves to a mailer/host/user triple.
-Ruleset 0 is rather lengthy
-as it has to handle 3 basic address forms:
-RFC976 bang paths,
-RFC1123 %-hacks
-(including vanilla RFC822 local-part@domain),
-and RFC822 source routes.
-It's also complicated by having to handle named lists.
-.pp
-The header rewriting rulesets 1 and 2
-remove the trailing "@.UUCP" that hides bang paths.
-Ruleset 2 also strips the $# mailer $@ host (for test mode).
-.pp
-Ruleset 4 does absolutely nothing.
-.pp
-The per-mailer rewriting rulesets conform the envelope and
-header addresses to the requirements of the specific
-mailer.
-.pp
-Lots of rulesets-as-subroutines are used.
-.pp
-As a result, header addresses are subject to minimal munging
-(per RFC1123), and the general plan is per RFC822 sect. 3.4.10.
-.sh 3 "Relevant issues"
-.pp
-The canonical form you use
-should almost certainly be as specified in
-the Internet protocols
-RFC819 and RFC822.
-Copies of these RFC's are included on the
-.i sendmail
-tape
-as
-.i doc/rfc819.lpr
-and
-.i doc/rfc822.lpr .
-.pp
-RFC822
-describes the format of the mail message itself.
-.i Sendmail
-follows this RFC closely,
-to the extent that many of the standards described in this document
-can not be changed without changing the code.
-In particular,
-the following characters have special interpretations:
-.(b
-< > ( ) " \e
-.)b
-Any attempt to use these characters for other than their RFC822
-purpose in addresses is probably doomed to disaster.
-.pp
-RFC819
-describes the specifics of the domain-based addressing.
-This is touched on in RFC822 as well.
-Essentially each host is given a name
-which is a right-to-left dot qualified pseudo-path
-from a distinguished root.
-The elements of the path need not be physical hosts;
-the domain is logical rather than physical.
-For example,
-at Berkeley
-one legal host might be
-.q a.CC.Berkeley.EDU ;
-reading from right to left,
-.q EDU
-is a top level domain
-comprising educational institutions,
-.q Berkeley
-is a logical domain name,
-.q CC
-represents the Computer Center,
-(in this case a strictly logical entity),
-and
-.q a
-is a host in the Computer Center.
-.pp
-Beware when reading RFC819
-that there are a number of errors in it.
-.sh 3 "How to proceed"
-.pp
-Once you have decided on a philosophy,
-it is worth examining the available configuration tables
-to decide if any of them are close enough
-to steal major parts of.
-Even under the worst of conditions,
-there is a fair amount of boiler plate that can be collected safely.
-.pp
-The next step is to build ruleset three.
-This will be the hardest part of the job.
-Beware of doing too much to the address in this ruleset,
-since anything you do will reflect through
-to the message.
-In particular,
-stripping of local domains is best deferred,
-since this can leave you with addresses with no domain spec at all.
-Since
-.i sendmail
-likes to append the sending domain to addresses with no domain,
-this can change the semantics of addresses.
-Also try to avoid
-fully qualifying domains in this ruleset.
-Although technically legal,
-this can lead to unpleasantly and unnecessarily long addresses
-reflected into messages.
-The Berkeley configuration files
-define ruleset nine
-to qualify domain names and strip local domains.
-This is called from ruleset zero
-to get all addresses into a cleaner form.
-.pp
-Once you have ruleset three finished,
-the other rulesets should be relatively trivial.
-If you need hints,
-examine the supplied configuration tables.
-.sh 3 "Testing the rewriting rules \*- the \-bt flag"
-.pp
-When you build a configuration table,
-you can do a certain amount of testing
-using the
-.q "test mode"
-of
-.i sendmail .
-For example,
-you could invoke
-.i sendmail
-as:
-.(b
-sendmail \-bt \-Ctest.cf
-.)b
-which would read the configuration file
-.q test.cf
-and enter test mode.
-In this mode,
-you enter lines of the form:
-.(b
-rwset address
-.)b
-where
-.i rwset
-is the rewriting set you want to use
-and
-.i address
-is an address to apply the set to.
-Test mode shows you the steps it takes
-as it proceeds,
-finally showing you the address it ends up with.
-You may use a comma separated list of rwsets
-for sequential application of rules to an input.
-For example:
-.(b
-3,1,21,4 monet:bollard
-.)b
-first applies ruleset three to the input
-.q monet:bollard.
-Ruleset one is then applied to the output of ruleset three,
-followed similarly by rulesets twenty-one and four.
-.pp
-If you need more detail,
-you can also use the
-.q \-d21
-flag to turn on more debugging.
-For example,
-.(b
-sendmail \-bt \-d21.99
-.)b
-turns on an incredible amount of information;
-a single word address
-is probably going to print out several pages worth of information.
-.pp
-You should be warned that internally,
-.i sendmail
-applies ruleset 3 to all addresses.
-In this version of
-.i sendmail ,
-you will have to do that manually.
-For example, older versions allowed you to use
-.(b
-0 bruce@broadcast.sony.com
-.)b
-This version requires that you use:
-.(b
-3,0 bruce@broadcast.sony.com
-.)b
-.sh 3 "Building mailer descriptions"
-.pp
-To add an outgoing mailer to your mail system,
-you will have to define the characteristics of the mailer.
-.pp
-Each mailer must have an internal name.
-This can be arbitrary,
-except that the names
-.q local
-and
-.q prog
-must be defined.
-.pp
-The pathname of the mailer must be given in the P field.
-If this mailer should be accessed via an IPC connection,
-use the string
-.q [IPC]
-instead.
-.pp
-The F field defines the mailer flags.
-You should specify an
-.q f
-or
-.q r
-flag to pass the name of the sender as a
-.b \-f
-or
-.b \-r
-flag respectively.
-These flags are only passed if they were passed to
-.i sendmail ,
-so that mailers that give errors under some circumstances
-can be placated.
-If the mailer is not picky
-you can just specify
-.q "\-f $g"
-in the argv template.
-If the mailer must be called as
-.b root
-the
-.q S
-flag should be given;
-this will not reset the userid
-before calling the mailer\**.
-.(f
-\**\c
-.i Sendmail
-must be running setuid to root
-for this to work.
-.)f
-If this mailer is local
-(i.e., will perform final delivery
-rather than another network hop)
-the
-.q l
-flag should be given.
-Quote characters
-(backslashes and " marks)
-can be stripped from addresses if the
-.q s
-flag is specified;
-if this is not given
-they are passed through.
-If the mailer is capable of sending to more than one user
-on the same host
-in a single transaction
-the
-.q m
-flag should be stated.
-If this flag is on,
-then the argv template containing
-.b $u
-will be repeated for each unique user
-on a given host.
-The
-.q e
-flag will mark the mailer as being
-.q expensive,
-which will cause
-.i sendmail
-to defer connection
-until a queue run\**.
-.(f
-\**The
-.q c
-configuration option must be given
-for this to be effective.
-.)f
-.pp
-An unusual case is the
-.q C
-flag.
-This flag applies to the mailer that the message is received from,
-rather than the mailer being sent to;
-if set,
-the domain spec of the sender
-(i.e., the
-.q @host.domain
-part)
-is saved
-and is appended to any addresses in the message
-that do not already contain a domain spec.
-For example,
-a message of the form:
-.(b
-From: eric@vangogh.CS.Berkeley.EDU
-To: wnj@monet.CS.Berkeley.EDU, mckusick
-.)b
-will be modified to:
-.(b
-From: eric@vangogh.CS.Berkeley.EDU
-To: wnj@monet.CS.Berkeley.EDU, mckusick@vangogh.CS.Berkeley.EDU
-.)b
-.i "if and only if"
-the
-.q C
-flag is defined in the mailer resolved to
-by running
-.q eric@vangogh.CS.Berkeley.EDU
-through rulesets 3 and 0.
-.pp
-Other flags are described
-in Appendix C.
-.pp
-The S and R fields in the mailer description
-are per-mailer rewriting sets
-to be applied to sender and recipient addresses
-respectively.
-These are applied after the sending domain is appended
-and the general rewriting sets
-(numbers one and two)
-are applied,
-but before the output rewrite
-(ruleset four)
-is applied.
-A typical use is to append the current domain
-to addresses that do not already have a domain.
-For example,
-a header of the form:
-.(b
-From: eric
-.)b
-might be changed to be:
-.(b
-From: eric@vangogh.CS.Berkeley.EDU
-.)b
-or
+For example, the map specification
 .(b
-From: ucbvax!eric
+Kuucp dbm \-o \-N /usr/lib/uucpmap
 .)b
-depending on the domain it is being shipped into.
-These sets can also be used
-to do special purpose output rewriting
-in cooperation with ruleset four.
+specifies an optional map named
+.q uucp
+of class
+.q dbm ;
+it always has null bytes at the end of every string,
+and the data is located in
+/usr/lib/uucpmap.{dir,pag}.
 .pp
-The S and R fields
-can be specified as two numbers separated by a slash
-(e.g.,
-.q "S=10/11" ),
-meaning that all envelope addresses will be processed through ruleset 10
-and all header addresses will be processed through ruleset 11.
-With only one number specified,
-both envelope and header rewriting sets are set to the indicated ruleset.
-.pp
-The E field defines the string to use
-as an end-of-line indication.
-A string containing only newline is the default.
-The usual backslash escapes
-(\er, \en, \ef, \eb)
-may be used.
-.pp
-Finally,
-an argv template is given as the A field.
-It may have embedded spaces.
-If there is no argv with a
-.b $u
-macro in it,
+The program
+.i makemap (8)
+can be used to build any of the three database-oriented maps.
+It takes the following flags:
+.ip \-f
+Do not fold upper to lower case in the map.
+.ip \-N
+Include null bytes in keys.
+.ip \-o
+Append to an existing (old) file.
+.ip \-r
+Allow replacement of existing keys;
+normally, re-inserting an existing key is an error.
+.ip \-v
+Print what is happening.
+.lp
+The
 .i sendmail
-will speak SMTP
-to the mailer.
-If the pathname for this mailer is
-.q [IPC],
-the argv should be
-.(b
-IPC $h [ \fIport\fP ]
-.)b
-where
-.i port
-is the optional port number
-to connect to.
+daemon does not have to be restarted to read the new maps
+as long as you change them in place;
+file locking is used so that the maps won't be read
+while they are being updated.\**
+.(f
+\**That is, don't create new maps and then use
+.i mv (1)
+to move them into place.
+Since the maps are already open
+the new maps will never be seen.
+.)f
 .pp
-For example,
-the specifications:
-.(b
-.ta \w'Mlocal, 'u +\w'P=/bin/mail, 'u +\w'F=rlsm, 'u +\w'S=10, 'u +\w'R=20, 'u
-Mlocal,        P=/bin/mail,    F=rlsm  S=10,   R=20,   A=mail \-d $u
-Mether,        P=[IPC],        F=meC,  S=11,   R=21,   A=IPC $h, M=100000
-.)b
-specifies a mailer to do local delivery
-and a mailer for ethernet delivery.
-The first is called
-.q local,
-is located in the file
-.q /bin/mail,
-takes a picky
-.b \-r
-flag,
-does local delivery,
-quotes should be stripped from addresses,
-and multiple users can be delivered at once;
-ruleset ten
-should be applied to sender addresses in the message
-and ruleset twenty
-should be applied to recipient addresses;
-the argv to send to a message will be the word
-.q mail,
-the word
-.q \-d,
-and words containing the name of the receiving user.
-If a
-.b \-r
-flag is inserted
-it will be between the words
-.q mail
-and
-.q \-d.
-The second mailer is called
-.q ether,
-it should be connected to via an IPC connection,
-it can handle multiple users at once,
-connections should be deferred,
-and any domain from the sender address
-should be appended to any receiver name
-without a domain;
-sender addresses should be processed by ruleset eleven
-and recipient addresses by ruleset twenty-one.
-There is a 100,000 byte limit on messages passed through this mailer.
+New classes can be added in the routine
+.b setupmaps
+in file
+.b conf.c .
 .sh 2 "The User Database"
 .pp
 If you have a version of
@@ -5161,7 +5732,7 @@ the handling of sender and recipient addresses
 is modified.
 .pp
 The location of this database is controlled with the
-.b U
+.b UserDatabaseSpec
 option.
 .sh 3 "Structure of the user database"
 .pp
@@ -5314,6 +5885,10 @@ recompiling
 .i sendmail .
 This section describes what changes can be made
 and what has to be modified to make them.
+In most cases this should be unnecessary
+unless you are porting
+.i sendmail
+to a new environment.
 .sh 2 "Parameters in src/Makefile"
 .pp
 These parameters are intended to describe the compilation environment,
@@ -5339,113 +5914,22 @@ If set together with
 NEWDB and NDBM,
 .i sendmail
 will create both DBM and NEWDB files if and only if
-the file /var/yp/Makefile
-exists and is readable.
+an alias file includes the substring
+.q /yp/
+in the name.
 This is intended for compatibility with Sun Microsystems'
 .i mkalias
 program used on YP masters.
-.ip SYSTEM5
-Set all of the compilation parameters appropriate for System V.
-.ip LOCKF
-Use System V
-.b lockf
-instead of Berkeley
-.b flock .
-Due to the highly unusual semantics of locks
-across forks in
-.b lockf ,
-this should never be used unless absolutely necessary.
-Set by default if
-SYSTEM5 is set.
-.ip SYS5TZ
-Use System V
-time zone semantics.
-.ip HASINITGROUPS
-Set this if your system has the
-.i initgroups()
-call
-(if you have multiple group support).
-This is the default if SYSTEM5 is
-.i not
-defined or if you are on HPUX.
-.ip HASUNAME
-Set this if you have the
-.i uname (2)
-system call (or corresponding library routine).
-Set by default if
-SYSTEM5
-is set.
-.ip HASSTATFS
-Set this if you have the
-.i statfs (2)
-system call.
-This will allow you to give a temporary failure
-message to incoming SMTP email
-when you are low on disk space.
-It is set by default on 4.4BSD and OSF/1 systems.
-.ip HASUSTAT
-Set if you have the
-.i ustat (2)
-system call.
-This is an alternative implementation of disk space control.
-You should only set one of HASSTATFS or HASUSTAT;
-the first is preferred.
+.ip NISPLUS
+Compile in support for NIS+.
+.ip NETINFO
+Compile in support for NetInfo (NeXT stations).
+.ip HESIOD
+Compile in support for Hesiod.
 .ip _PATH_SENDMAILCF
 The pathname of the sendmail.cf file.
 .ip _PATH_SENDMAILPID
 The pathname of the sendmail.pid file.
-.ip LA_TYPE
-The load average type.
-Details are described below.
-.lp
-The are several built-in ways of computing the load average.
-.i Sendmail
-tries to auto-configure them based on imperfect guesses;
-you can select one using the
-.i cc
-option
-.b \-DLA_TYPE= \c
-.i type ,
-where
-.i type
-is:
-.ip LA_INT
-The kernel stores the load average in the kernel as an array of long integers.
-The actual values are scaled by a factor FSCALE
-(default 256).
-.ip LA_SHORT
-The kernel stores the load average in the kernel as an array of short integers.
-The actual values are scaled by a factor FSCALE
-(default 256).
-.ip LA_FLOAT
-The kernel stores the load average in the kernel as an array of
-double precision floats.
-.ip LA_MACH
-Use MACH-style load averages.
-.ip LA_SUBR
-Call the
-.i getloadavg
-routine to get the load average as an array of doubles.
-.ip LA_ZERO
-Always return zero as the load average.
-This is the fallback case.
-.lp
-If type
-.sm LA_INT ,
-.sm LA_SHORT ,
-or
-.sm LA_FLOAT
-is specified,
-you may also need to specify
-.sm _PATH_UNIX
-(the path to your system binary)
-and
-.sm LA_AVENRUN
-(the name of the variable containing the load average in the kernel;
-usually
-.q _avenrun
-or
-.q avenrun ).
 .pp
 There are also several compilation flags to indicate the environment
 such as
@@ -5464,8 +5948,12 @@ However, the sizes of certain primitive vectors, etc.,
 are included in this file.
 The numbers following the parameters
 are their default value.
+.pp
+This document is not the best source of information
+for compilation flags in conf.h \(em
+see src/READ_ME or src/conf.h itself.
 .nr ii 1.2i
-.ip "MAXLINE [1024]"
+.ip "MAXLINE [2048]"
 The maximum line length of any input line.
 If message lines exceed this length
 they will still be processed correctly;
@@ -5496,9 +5984,15 @@ is seven atoms.
 .ip "MAXMAILERS [25]"
 The maximum number of mailers that may be defined
 in the configuration file.
-.ip "MAXRWSETS [100]"
+.ip "MAXRWSETS [200]"
 The maximum number of rewriting sets
 that may be defined.
+The first half of these are reserved for numeric specification
+(e.g., ``S92''),
+while the upper half are reserved for auto-numbering
+(e.g., ``Sfoo'').
+Thus, with a value of 200 an attempt to use ``S99'' will succeed,
+but ``S100'' will fail.
 .ip "MAXPRIORITIES [25]"
 The maximum number of values for the
 .q Precedence:
@@ -5506,29 +6000,32 @@ field that may be defined
 (using the
 .b P
 line in sendmail.cf).
-.ip "MAXUSERENVIRON [40]"
+.ip "MAXUSERENVIRON [100]"
 The maximum number of items in the user environment
 that will be passed to subordinate mailers.
-.ip "QUEUESIZE [1000]"
-The maximum number of entries that will be processed
-in a single queue run.
 .ip "MAXMXHOSTS [20]"
 The maximum number of MX records we will accept for any single host.
+.ip "MAXALIASDB [12]"
+The maximum number of alias databases that can be open at any time.
+Note that there may also be an open file limit.
+.ip "MAXMAPSTACK [12]"
+The maximum number of maps that may be "stacked" in a
+.b sequence
+class map.
+.ip "MAXMIMEARGS [20]"
+The maximum number of arguments in a MIME Content-Type: header;
+additional arguments will be ignored.
+.ip "MAXMIMENESTING [20]"
+The maximum depth to which MIME messages may be nested
+(that is, nested Message or Multipart documents;
+this does not limit the number of components in a single Multipart document).
 .lp
 A number of other compilation options exist.
 These specify whether or not specific code should be compiled in.
+Ones marked with \(dg
+are 0/1 valued.
 .nr ii 1.2i
-.ip DEBUG
-If set, debugging information is compiled in.
-To actually get the debugging output,
-the
-.b \-d
-flag must be used.
-.b "WE STRONGLY RECOMMEND THAT THIS BE LEFT ON."
-Some people, believing that it was a security hole
-(it was, once)
-have turned it off and thus crippled debuggers.
-.ip NETINET
+.ip NETINET\(dg
 If set,
 support for Internet protocol networking is compiled in.
 Previous versions of
@@ -5536,7 +6033,10 @@ Previous versions of
 referred to this as
 .sm DAEMON ;
 this old usage is now incorrect.
-.ip NETISO
+Defaults on;
+turn it off in the Makefile
+if your system doesn't support the Internet protocols.
+.ip NETISO\(dg
 If set,
 support for ISO protocol networking is compiled in
 (it may be appropriate to #define this in the Makefile instead of conf.h).
@@ -5549,11 +6049,15 @@ This makes an informational log record
 for each message processed,
 and makes a higher priority log record
 for internal system errors.
-.ip MATCHGECOS
+.b "STRONGLY RECOMMENDED"
+\(em if you want no logging, turn it off in the configuration file.
+.ip MATCHGECOS\(dg
 Compile in the code to do ``fuzzy matching'' on the GECOS field
 in /etc/passwd.
-This also requires that option G be turned on.
-.ip NAMED_BIND
+This also requires that the
+.b MatchGECOS
+option be turned on.
+.ip NAMED_BIND\(dg
 Compile in code to use the
 Berkeley Internet Name Domain (BIND) server
 to resolve TCP/IP host names.
@@ -5568,30 +6072,13 @@ This flag should be set to compile in the queueing code.
 If this is not set,
 mailers must accept the mail immediately
 or it will be returned to the sender.
-.ip SETPROCTITLE
-If defined,
-.i sendmail
-will change its
-.i argv
-array to indicate its current status.
-This can be used in conjunction with the
-.i ps
-command to find out just what it's up to.
 .ip SMTP
 If set,
 the code to handle user and server SMTP will be compiled in.
 This is only necessary if your machine has some mailer
 that speaks SMTP
 (this means most machines everywhere).
-.ip UGLYUUCP
-If you have a UUCP host adjacent to you which is not running
-a reasonable version of
-.i rmail ,
-you will have to set this flag to include the
-.q "remote from sysname"
-info on the from line.
-Otherwise, UUCP gets confused about where the mail came from.
-.ip USERDB
+.ip USERDB\(dg
 Include the
 .b experimental
 Berkeley user information database package.
@@ -5599,7 +6086,10 @@ This adds a new level of local name expansion
 between aliasing and forwarding.
 It also uses the NEWDB package.
 This may change in future releases.
-.ip IDENTPROTO
+.lp
+The following options are normally turned on
+in per-operating-system clauses in conf.h.
+.ip IDENTPROTO\(dg
 Compile in the IDENT protocol as defined in RFC 1413.
 This defaults on for all systems except Ultrix,
 which apparently has the interesting
@@ -5610,6 +6100,104 @@ message it closes all open connections to that host.
 Since some firewall gateways send this error code
 when you access an unauthorized port (such as 113, used by IDENT),
 Ultrix cannot receive email from such hosts.
+.ip SYSTEM5
+Set all of the compilation parameters appropriate for System V.
+.ip HASFLOCK\(dg
+Use Berkeley-style
+.b flock
+instead of System V
+.b lockf
+to do file locking.
+Due to the highly unusual semantics of locks
+across forks in
+.b lockf ,
+this should always be used if at all possible.
+.ip HASINITGROUPS
+Set this if your system has the
+.i initgroups()
+call
+(if you have multiple group support).
+This is the default if SYSTEM5 is
+.i not
+defined or if you are on HPUX.
+.ip HASUNAME
+Set this if you have the
+.i uname (2)
+system call (or corresponding library routine).
+Set by default if
+SYSTEM5
+is set.
+.ip HASGETDTABLESIZE
+Set this if you have the
+.i getdtablesize (2)
+system call.
+.ip HASWAITPID
+Set this if you have the
+.i haswaitpid (2)
+system call.
+.ip SFS_TYPE
+The mechanism that can be used to get file system capacity information.
+The values can be one of
+SFS_USTAT (use the ustat(2) syscall),
+SFS_4ARGS (use the four argument statfs(2) syscall),
+SFS_VFS (use the two argument statfs(2) syscall including <sys/vfs.h>),
+SFS_MOUNT (use the two argument statfs(2) syscall including <sys/mount.h>),
+SFS_STATFS (use the two argument statfs(2) syscall including <sys/statfs.h>),
+SFS_STATVFS (use the two argument statfs(2) syscall including <sys/statvfs.h>),
+or
+SFS_NONE (no way to get this information).
+.ip LA_TYPE
+The load average type.
+Details are described below.
+.lp
+The are several built-in ways of computing the load average.
+.i Sendmail
+tries to auto-configure them based on imperfect guesses;
+you can select one using the
+.i cc
+option
+.b \-DLA_TYPE= \c
+.i type ,
+where
+.i type
+is:
+.ip LA_INT
+The kernel stores the load average in the kernel as an array of long integers.
+The actual values are scaled by a factor FSCALE
+(default 256).
+.ip LA_SHORT
+The kernel stores the load average in the kernel as an array of short integers.
+The actual values are scaled by a factor FSCALE
+(default 256).
+.ip LA_FLOAT
+The kernel stores the load average in the kernel as an array of
+double precision floats.
+.ip LA_MACH
+Use MACH-style load averages.
+.ip LA_SUBR
+Call the
+.i getloadavg
+routine to get the load average as an array of doubles.
+.ip LA_ZERO
+Always return zero as the load average.
+This is the fallback case.
+.lp
+If type
+.sm LA_INT ,
+.sm LA_SHORT ,
+or
+.sm LA_FLOAT
+is specified,
+you may also need to specify
+.sm _PATH_UNIX
+(the path to your system binary)
+and
+.sm LA_AVENRUN
+(the name of the variable containing the load average in the kernel;
+usually
+.q _avenrun
+or
+.q avenrun ).
 .sh 2 "Configuration in src/conf.c"
 .pp
 The following changes can be made in conf.c.
@@ -5683,13 +6271,21 @@ table specifies
 .i sendmail 's
 preference
 for which field to return error messages to.
+.ip H_ERRORSTO
+Addresses in this header should receive error messages.
+.ip H_CTE
+This header is a Content-Transfer-Encoding header.
+.ip H_CTYPE
+This header is a Content-Type header.
+.ip H_STRIPVAL
+Strip the value from the header (for Bcc:).
 .nr ii 5n
 .lp
 Let's look at a sample
 .i HdrInfo
 specification:
 .(b
-.ta 4n +\w'"return-receipt-to",  'u
+.ta 4n +\w'"content-transfer-encoding",  'u
 struct hdrinfo HdrInfo[] =
 \&{
             /* originator fields, most to least significant  */
@@ -5698,15 +6294,20 @@ struct hdrinfo  HdrInfo[] =
        "sender",       H_FROM,
        "from", H_FROM,
        "full-name",    H_ACHECK,
+       "errors-to",    H_FROM\^|\^H_ERRORSTO,
             /* destination fields */
        "to",   H_RCPT,
        "resent-to",    H_RCPT,
        "cc",   H_RCPT,
+       "bcc",  H_RCPT\^|\^H_STRIPVAL,
             /* message identification and control */
        "message",      H_EOH,
        "text", H_EOH,
             /* trace fields */
-       "received",     H_TRACE|H_FORCE,
+       "received",     H_TRACE\^|\^H_FORCE,
+            /* miscellaneous fields */
+       "content-transfer-encoding",    H_CTE,
+       "content-type", H_CTYPE,
 
        NULL,   0,
 };
@@ -5824,10 +6425,10 @@ checkcompat(to, e)
                usrerr("No private net mail allowed through this machine");
                return (EX_UNAVAILABLE);
        }
-       if (MsgSize > 50000 && to\->q_mailer != LocalMailer)
+       if (MsgSize > 50000 && bitnset(M_LOCALMAILER, to\->q_mailer))
        {
                usrerr("Message too large for non-local delivery");
-               NoReturn = TRUE;
+               e\->e_flags |= EF_NORETURN;
                return (EX_UNAVAILABLE);
        }
        return (EX_OK);
@@ -5837,8 +6438,10 @@ checkcompat(to, e)
 This would reject messages greater than 50000 bytes
 unless they were local.
 The
-.i NoReturn
-flag can be sent to suppress the return of the actual body
+.i EF_NORETURN
+flag can be set in
+.i e\(->e_flags
+to suppress the return of the actual body
 of the message in the error return.
 The actual use of this routine is highly dependent on the
 implementation,
@@ -5849,7 +6452,7 @@ The routine
 .i getla
 should return an approximation of the current system load average
 as an integer.
-There are four versions included on compilation flags
+There are several versions included on compilation flags
 as described above.
 .sh 3 "New Database Map Classes"
 .pp
@@ -5917,8 +6520,6 @@ shouldqueue(pri, ctime)
 {
        if (CurrentLA < QueueLA)
                return (FALSE);
-       if (CurrentLA >= RefuseLA)
-               return (TRUE);
        return (pri > (QueueFactor / (CurrentLA \- QueueLA + 1)));
 }
 .)b
@@ -5999,6 +6600,12 @@ The routine
 .i getla
 returns the current load average (as a rounded integer).
 The distribution includes several possible implementations.
+If you are porting to a new environment
+you may need to add some new tweaks.\**
+.(f
+\**If you do, please send updates to
+sendmail@CS.Berkeley.EDU.
+.)f
 .sh 2 "Configuration in src/daemon.c"
 .pp
 The file
@@ -6021,7 +6628,13 @@ We now recommend that you create a new keyed map instead.
 The following summarizes changes
 since the last commonly available version of
 .i sendmail
-(5.67):
+(5.67).
+For a detailed list,
+consult the file
+RELEASE_NOTES
+in the root directory of the
+.i sendmail
+distribution.
 .sh 2 "Connection Caching"
 .pp
 Instead of closing SMTP connections immediately,
@@ -6088,8 +6701,8 @@ doesn't always provide adequate concurrency limits.
 .sh 2 "Extended SMTP Support"
 .pp
 Version 8 includes both sending and receiving support for Extended
-SMTP support as defined by RFC 1425 (basic) and RFC 1427 (SIZE);
-and limited support for RFC 1426 (BODY).
+SMTP support as defined by RFC 1651 (basic) and RFC 1653 (SIZE);
+and limited support for RFC 1652 (BODY).
 .sh 2 "Eight-Bit Clean"
 .pp
 Previous versions of
@@ -6199,6 +6812,10 @@ flag has been added
 to allow logging of all protocol in and out of
 .i sendmail
 for debugging.
+.pp
+The
+.b \-O
+flag simplies setting long-form options.
 .sh 2 "Enhanced Command Line Flags"
 .pp
 The
@@ -6216,11 +6833,6 @@ respectively.
 .sh 2 "New and Old Configuration Line Types"
 .pp
 The
-.b T
-(Trusted users) configuration line has been deleted.
-It will still be accepted but will be ignored.
-.pp
-The
 .b K
 line has been added to declare database maps.
 .pp
@@ -6234,6 +6846,10 @@ line has a
 .q D=
 field that lets you change into a temporary directory while that mailer
 is running.
+It also has a
+.q U=
+field to allow you to set the user and group id to be used
+when running the mailer.
 .sh 2 "New Options"
 .pp
 Several new options have been added,
@@ -6284,6 +6900,8 @@ host.
 handling technique.
 .ip 7
 Do not run eight bit clean.
+.ip 8
+Eight bit data handling mode.
 .sh 2 "Extended Options"
 .pp
 The
@@ -6309,6 +6927,8 @@ If the EHLO (extended hello)
 command fails,
 .i sendmail
 falls back to old SMTP.
+.ip A
+Try the user part of addresses for this mailer as aliases.
 .ip b
 Ensure that there is a blank line at the end of all messages.
 .ip c
@@ -6320,8 +6940,29 @@ Never use the null sender as the envelope sender,
 even when running SMTP.
 Although this violates RFC 1123,
 it may be necessary when you must deal with some obnoxious old hosts.
+.ip k
+Turn off the loopback check in the HELO protocol;
+doing this may cause mailer loops.
+.ip o
+Always run the mailer as the recipient of the message.
+.ip w
+This user should have a passwd file entry.
+.ip 5
+Try ruleset 5 if no local aliases.
 .ip 7
 Strip all output to 7 bits.
+.ip :
+Check for :include: files.
+.ip |
+Check for |program addresses.
+.ip /
+Check for /file addresses.
+.ip @
+Check this user against the user database.
+.sh 2 "Long Option Names"
+.pp
+All options can be specified using long names,
+and some new options can only be specified with long names.
 .sh 2 "New Pre-Defined Macros"
 .pp
 The following macros are pre-defined:
@@ -6443,9 +7084,10 @@ for many years,
 and many employers have been remarkably patient
 about letting me work on a large project
 that was not part of my official job.
-This includes time on the INGRES Project at Berkeley,
+This includes time on the INGRES Project at
+the University of California at Berkeley,
 at Britton Lee,
-and again on the Mammoth Project at Berkeley.
+and again on the Mammoth and Titan Projects at Berkeley.
 .pp
 Much of the second wave of improvements
 should be credited to Bryan Costales of ICSI.
@@ -6460,7 +7102,9 @@ It has proven to be a group network effort.
 Version 8 in particular was a group project.
 The following people made notable contributions:
 .(l
+John Beck, Hewlett-Packard
 Keith Bostic, CSRG, University of California, Berkeley
+Andrew Cheng, Sun Microsystems
 Michael J. Corrigan, University of California, San Diego
 Bryan Costales, International Computer Science Institute
 Pa\*:r (Pell) Emanuelsson
@@ -6473,17 +7117,26 @@ Brian Kantor, University of California, San Diego
 Murray S. Kucherawy, HookUp Communication Corp.
 Bruce Lilly, Sony U.S.
 Karl London
-Nakamura Motonori, Kyoto University
+Motonori Nakamura, Ritsumeikan University & Kyoto University
 John Gardiner Myers, Carnegie Mellon University
 Neil Rickert, Northern Illinois University
 Eric Schnoebelen, Convex Computer Corp.
 Eric Wassenaar, National Institute for Nuclear and High Energy Physics, Amsterdam
-Christophe Wolfhugel, Herve Schauer Consultants (Paris)
+Christophe Wolfhugel, Pasteur Institute & Herve Schauer Consultants (Paris)
 .)l
 I apologize for anyone I have omitted, misspelled, misattributed, or
 otherwise missed.
-Many other people have contributed ideas, comments, and encouragement.
+At this point, I suspect that at least a hundred people
+have contributed code,
+and many more have contributed ideas, comments, and encouragement.
+I've tried to list them in the RELEASE_NOTES in the distribution directory.
 I appreciate their contribution as well.
+.pp
+Special thanks are reserved for Michael Corrigan and Christophe Wolfhugel,
+who besides being wonderful guinea pigs and contributors
+have also consented to be added to the ``sendmail@CS.Berkeley.EDU'' list
+and, by answering the bulk of the questions sent to that list,
+have freed me up to do other work.
 .++ A
 .+c "COMMAND LINE FLAGS"
 .ba 0
@@ -6499,12 +7152,16 @@ Operation modes are:
 .ta 4n
 m      Deliver mail (default)
 s      Speak SMTP on input side
+a\(dg  ``Arpanet'' mode (get envelope sender information from header)
 d      Run as a daemon
 t      Run in test mode
 v      Just verify addresses, don't collect or deliver
 i      Initialize the alias database
 p      Print the mail queue
 .)b
+.(f
+\(dgDeprecated.
+.)f
 .ip \-B\fItype\fP
 Indicate body type.
 .ip \-C\fIfile\fP
@@ -6546,7 +7203,19 @@ Set option
 .i x
 to the specified
 .i value .
-These options are described in Appendix B.
+These options are described in Section 5.6.
+.ip \-O\fIoption\fP\fB=\fP\fIvalue\fP
+Set
+.i option
+to the specified
+.i value
+(for long form option names).
+These options are described in Section 5.6.
+.ip \-M\fIx\|value
+Set macro
+.i x
+to the specified
+.i value .
 .ip \-p\fIprotocol\fP
 Set the sending protocol.
 Programs are encouraged to set this.
@@ -6646,31 +7315,6 @@ The types are:
 .ip d
 The data file.
 The message body (excluding the header) is kept in this file.
-.ip l
-The lock file.
-If this file exists,
-the job is currently being processed,
-and a queue run will not process the file.
-For that reason,
-an extraneous
-.b lf
-file can cause a job to apparently disappear
-(it will not even time out!).
-[Actually, this file is obsolete on most systems that support the
-.b flock
-or
-.b lockf
-system calls.]
-.ip n
-This file is created when an id is being created.
-It is a separate file to insure that no mail can ever be destroyed
-due to a race condition.
-It should exist for no more than a few milliseconds
-at any given time.
-[This is only used on old versions of
-.i sendmail ;
-it is not used 
-on newer versions.]
 .ip q
 The queue control file.
 This file contains the information necessary to process the job.
@@ -6693,9 +7337,13 @@ The
 file is structured as a series of lines
 each beginning with a code letter.
 The lines are as follows:
-.ip D
-The name of the data file.
-There may only be one of these lines.
+.ip V
+The version number of the queue file format,
+used to allow new
+.i sendmail
+binaries to read queue files created by older versions.
+Defaults to version zero.
+Must be the first line of the file if present.
 .ip H
 A header definition.
 There may be any number of these lines.
@@ -6714,19 +7362,30 @@ will be flagged so that deliveries will be run as the
 .i aliasname
 is the name of the alias that expanded to this address
 (used for printing messages).
+.ip Q
+The ``original recipient'',
+specified by the ORCPT= field in an ESMTP transaction.
+Used exclusively for Delivery Status Notifications.
+It applies only to the immediately following `R' line.
 .ip R
 A recipient address.
 This will normally be completely aliased,
 but is actually realiased when the job is processed.
 There will be one line
 for each recipient.
+Version 1 qf files
+also include a leading colon-terminated list of flags,
+which can be
+`S' to return a message on successful final delivery,
+`F' to return a message on failure,
+`D' to return a message if the message is delayed,
+`B' to indicate that the body should be returned,
+`N' to suppress returning the body,
+and
+`P' to declare this as a ``primary'' (command line or SMTP-session) address.
 .ip S
 The sender address.
 There may only be one of these lines.
-.ip E
-An error address.
-If any such lines exist,
-they represent the addresses that should receive error messages.
 .ip T
 The job creation time.
 This is used to compute when to time out the job.
@@ -6754,6 +7413,15 @@ and
 .b w
 indicating that a warning message has been sent
 announcing that the mail has been delayed.
+.ip N
+The total number of delivery attempts.
+.ip K
+The time (as seconds since January 1, 1970)
+of the last delivery attempt.
+.ip I
+The i-number of the data file;
+this can be used to recover your mail queue
+after a disastrous disk crash.
 .ip $
 A macro definition.
 The values of certain macros
@@ -6773,6 +7441,12 @@ Legal values are
 .q 7BIT
 and
 .q 8BITMIME .
+.ip O
+The original MTS value (from the ESMTP transaction).
+For Deliver Status Notifications only.
+.ip Z
+The original envelope id (from the ESMTP transaction).
+For Deliver Status Notifications only.
 .pp
 As an example,
 the following is a queue file sent to
@@ -6787,9 +7461,7 @@ nothing can replace looking at what your own system generates.
 .(b
 P835771
 T404261372
-DdfAAA13557
 Seric
-Eowner-sendmail@vangogh.CS.Berkeley.EDU
 Ceric:sendmail@vangogh.CS.Berkeley.EDU
 Reric@mammoth.Berkeley.EDU
 Rbostic@okeeffe.CS.Berkeley.EDU
@@ -6808,7 +7480,7 @@ Hmessage-id: <9207170931.AA22757@foo.bar.baz.de>
 HTo: sendmail@vangogh.CS.Berkeley.EDU
 Hsubject: this is an example message
 .)b
-This shows the name of the data file,
+This shows
 the person who sent the message,
 the submission time
 (in seconds since January 1, 1970),
@@ -6891,7 +7563,7 @@ A transcript of the current session.
 .\"Eric Allman
 .\"Britton-Lee, Inc.
 .\".sp
-.\"Version 8.36
+.\"Version 8.70
 .\".ce 0
 .bp 2
 .rs
index 1e11b01..97ec33b 100644 (file)
@@ -40,7 +40,7 @@ static char copyright[] =
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)mailstats.c        8.3 (Berkeley) 12/27/93";
+static char sccsid[] = "@(#)mailstats.c        8.4 (Berkeley) 8/14/94";
 #endif /* not lint */
 
 #include <sendmail.h>
@@ -126,14 +126,32 @@ main(argc, argv)
                        break;
 
                  case 'O':             /* option -- see if .st file */
-                       if (*b++ != 'S')
+                       if (strncasecmp(b, " StatusFile", 11) == 0 &&
+                           !isalnum(b[11]))
+                       {
+                               /* new form -- find value */
+                               b = strchr(b, '=');
+                               if (b == NULL)
+                                       continue;
+                               while (isspace(*++b))
+                                       continue;
+                       }
+                       else if (*b++ != 'S')
+                       {
+                               /* something else boring */
                                continue;
+                       }
 
-                       /* yep -- save this */
+                       /* this is the S or StatusFile option -- save it */
                        strcpy(sfilebuf, b);
-                       b = strchr(sfilebuf, '\n');
-                       if (b != NULL)
-                               *b = '\0';
+                       b = strchr(sfilebuf, '#');
+                       if (b == NULL)
+                               b = strchr(sfilebuf, '\n');
+                       if (b == NULL)
+                               b = &sfilebuf[strlen(sfilebuf)];
+                       while (isspace(*--b))
+                               continue;
+                       *++b = '\0';
                        if (sfile == NULL)
                                sfile = sfilebuf;
 
index 2ee45c2..23ac522 100644 (file)
@@ -29,7 +29,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\"     @(#)makemap.8  8.2 (Berkeley) 9/22/93
+.\"     @(#)makemap.8  8.3 (Berkeley) 7/24/94
 .\"
 .Dd November 16, 1992
 .Dt MAKEMAP 8
@@ -40,6 +40,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl N
+.Op Fl d
 .Op Fl f
 .Op Fl o
 .Op Fl r
@@ -98,6 +99,11 @@ Include the null byte that terminates strings
 in the map.
 This must match the \-N flag in the sendmail.cf
 ``K'' line.
+.It Fl d
+Allow duplicate keys in the map.
+This is only allowed on B-Tree format maps.
+If two identical keys are read,
+they will both be inserted into the map.
 .It Fl f
 Normally all upper case letters in the key
 are folded to lower case.
index a676cd7..371b229 100644 (file)
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)makemap.c  8.6.1.1 (Berkeley) 3/6/95";
+static char sccsid[] = "@(#)makemap.c  8.14 (Berkeley) 11/5/95";
 #endif /* not lint */
 
 #include <stdio.h>
 #include <sysexits.h>
 #include <sys/types.h>
-#include <sys/file.h>
 #include <ctype.h>
 #include <string.h>
+#include <sys/errno.h>
+#ifndef ISC_UNIX
+# include <sys/file.h>
+#endif
 #include "useful.h"
 #include "conf.h"
 
@@ -80,6 +83,7 @@ main(argc, argv)
        bool inclnull = FALSE;
        bool notrunc = FALSE;
        bool allowreplace = FALSE;
+       bool allowdups = FALSE;
        bool verbose = FALSE;
        bool foldcase = TRUE;
        int exitstat;
@@ -87,10 +91,12 @@ main(argc, argv)
        char *typename;
        char *mapname;
        char *ext;
+       char *lext;
        int lineno;
        int st;
        int mode;
        enum type type;
+       int fd;
        union
        {
 #ifdef NDBM
@@ -102,14 +108,19 @@ main(argc, argv)
                void    *dbx;
        } dbp;
        union dbent key, val;
+#ifdef NEWDB
+       BTREEINFO bti;
+#endif
        char ibuf[BUFSIZE];
        char fbuf[MAXNAME];
+       char lbuf[MAXNAME];
        extern char *optarg;
        extern int optind;
+       extern bool lockfile();
 
        progname = argv[0];
 
-       while ((opt = getopt(argc, argv, "Nforv")) != EOF)
+       while ((opt = getopt(argc, argv, "Ndforv")) != EOF)
        {
                switch (opt)
                {
@@ -117,6 +128,10 @@ main(argc, argv)
                        inclnull = TRUE;
                        break;
 
+                 case 'd':
+                       allowdups = TRUE;
+                       break;
+
                  case 'f':
                        foldcase = FALSE;
                        break;
@@ -148,10 +163,12 @@ main(argc, argv)
                typename = argv[0];
                mapname = argv[1];
                ext = NULL;
+               lext = NULL;
 
                if (strcmp(typename, "dbm") == 0)
                {
                        type = T_DBM;
+                       lext = ".dir";
                }
                else if (strcmp(typename, "btree") == 0)
                {
@@ -170,7 +187,7 @@ main(argc, argv)
        switch (type)
        {
          case T_ERR:
-               fprintf(stderr, "Usage: %s [-N] [-o] [-v] type mapname\n", progname);
+               fprintf(stderr, "Usage: %s [-N] [-d] [-f] [-o] [-r] [-v] type mapname\n", progname);
                exit(EX_USAGE);
 
          case T_UNKNOWN:
@@ -188,6 +205,26 @@ main(argc, argv)
                fprintf(stderr, "%s: Type %s not supported in this version\n",
                        progname, typename);
                exit(EX_UNAVAILABLE);
+
+#ifdef NEWDB
+         case T_BTREE:
+               bzero(&bti, sizeof bti);
+               if (allowdups)
+                       bti.flags |= R_DUP;
+               break;
+
+         case T_HASH:
+#endif
+#ifdef NDBM
+         case T_DBM:
+#endif
+               if (allowdups)
+               {
+                       fprintf(stderr, "%s: Type %s does not support -d (allow dups)\n",
+                               progname, typename);
+                       exit(EX_UNAVAILABLE);
+               }
+               break;
        }
 
        /*
@@ -208,6 +245,10 @@ main(argc, argv)
                }
        }
 
+       strcpy(lbuf, mapname);
+       if (lext != NULL)
+               strcat(lbuf, lext);
+
        /*
        **  Create the database.
        */
@@ -215,6 +256,19 @@ main(argc, argv)
        mode = O_RDWR;
        if (!notrunc)
                mode |= O_CREAT|O_TRUNC;
+#ifdef O_EXLOCK
+       mode |= O_EXLOCK;
+#else
+       /* pre-lock the database */
+       fd = open(lbuf, mode & ~O_TRUNC, 0644);
+       if (fd < 0)
+       {
+               fprintf(stderr, "%s: cannot create type %s map %s\n",
+                       progname, typename, mapname);
+               exit(EX_CANTCREAT);
+       }
+       (void) lockfile(fd);
+#endif
        switch (type)
        {
 #ifdef NDBM
@@ -227,13 +281,25 @@ main(argc, argv)
          case T_HASH:
                dbp.db = dbopen(mapname, mode, 0644, DB_HASH, NULL);
                if (dbp.db != NULL)
+               {
+# if OLD_NEWDB
+                       (void) (*dbp.db->sync)(dbp.db);
+# else
                        (void) (*dbp.db->sync)(dbp.db, 0);
+# endif
+               }
                break;
 
          case T_BTREE:
-               dbp.db = dbopen(mapname, mode, 0644, DB_BTREE, NULL);
+               dbp.db = dbopen(mapname, mode, 0644, DB_BTREE, &bti);
                if (dbp.db != NULL)
+               {
+# if OLD_NEWDB
+                       (void) (*dbp.db->sync)(dbp.db);
+# else
                        (void) (*dbp.db->sync)(dbp.db, 0);
+# endif
+               }
                break;
 #endif
 
@@ -244,7 +310,7 @@ main(argc, argv)
 
        if (dbp.dbx == NULL)
        {
-               fprintf(stderr, "%s: cannot create type %s map %s\n",
+               fprintf(stderr, "%s: cannot open type %s map %s\n",
                        progname, typename, mapname);
                exit(EX_CANTCREAT);
        }
@@ -373,5 +439,58 @@ main(argc, argv)
 #endif
        }
 
+#ifndef O_EXLOCK
+       /* release locks */
+       close(fd);
+#endif
+
        exit (exitstat);
 }
+\f/*
+**  LOCKFILE -- lock a file using flock or (shudder) fcntl locking
+**
+**     Parameters:
+**             fd -- the file descriptor of the file.
+**
+**     Returns:
+**             TRUE if the lock was acquired.
+**             FALSE otherwise.
+*/
+
+bool
+lockfile(fd)
+       int fd;
+{
+# if !HASFLOCK
+       int action;
+       struct flock lfd;
+       extern int errno;
+
+       bzero(&lfd, sizeof lfd);
+       lfd.l_type = F_WRLCK;
+       action = F_SETLKW;
+
+       if (fcntl(fd, action, &lfd) >= 0)
+               return TRUE;
+
+       /*
+       **  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 (errno == EINVAL)
+               return TRUE;
+
+# else /* HASFLOCK */
+
+       if (flock(fd, LOCK_EX) >= 0)
+               return TRUE;
+
+# endif
+
+       return FALSE;
+}
index 192b91f..0a875a2 100644 (file)
@@ -1,9 +1,9 @@
-#
-#  NetBSD Makefile
-#
-#      @(#)Makefile.NetBSD     8.1 (Berkeley) 2/26/94
-#      @Id: Makefile.NetBSD,v 1.3 1994/02/01 05:33:44 glass Exp $
-#
+#      @(#)Makefile    8.7 (Berkeley) 10/31/95
+
+#########################################################################
+#  This Makefile is for 4.4BSD only!!!  For all other systems, use     #
+#  the "makesendmail" script.                                          #
+#########################################################################
 
 PROG=  sendmail
 
@@ -15,21 +15,22 @@ PROG=       sendmail
 # databases are read, but the new format will be used on any rebuilds.  On
 # really gnarly systems, you can set this to null; it will crawl like a high
 # spiral snail, but it will work.
-DBMDEF=        -DNEWDB -DNIS
+DBMDEF=        -DNEWDB
 
-CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO -D_PATH_VARTMP='"/var/tmp"'
+CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO
 
 SRCS=  alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \
        deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \
-       mci.c parseaddr.c queue.c readcf.c recipient.c savemail.c srvrsmtp.c \
-       stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \
+       mci.c mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c \
+       srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \
        util.c version.c
 MAN=   aliases.5 mailq.1 newaliases.1 sendmail.8
 LINKS= /usr/sbin/sendmail /usr/bin/newaliases \
        /usr/sbin/sendmail /usr/bin/mailq
 BINDIR=        /usr/sbin
 BINOWN=        root
-BINMODE=4555
+BINGRP=        kmem
+BINMODE=6555
 
 beforeinstall:
 #      install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
index db45f1a..ebeacd0 100644 (file)
@@ -18,7 +18,7 @@ PROG= sendmail
 DBMDEF=        -DNEWDB -DNIS
 
 #nasty warning about gcc 2.4.x caused bugs 
-CFLAGS=-I${.CURDIR} ${DBMDEF} -DNETISO -D_PATH_VARTMP='"/var/tmp"'
+CFLAGS=-I${.CURDIR} ${DBMDEF} -DNETISO
 #CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO
 
 SRCS=  alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \
index 192b91f..aa843bb 100644 (file)
@@ -17,7 +17,7 @@ PROG= sendmail
 # spiral snail, but it will work.
 DBMDEF=        -DNEWDB -DNIS
 
-CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO -D_PATH_VARTMP='"/var/tmp"'
+CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO
 
 SRCS=  alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \
        deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \
index ace0d3a..264b2eb 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 1983 Eric P. Allman
+# Copyright (c) 1983, 1995 Eric P. Allman
 # Copyright (c) 1988 The Regents of the University of California.
 # All rights reserved.
 #
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 #
-#      @(#)READ_ME     8.61.1.1 (Berkeley) 3/5/95
+#      @(#)READ_ME     8.113 (Berkeley) 11/29/95
 #
 
 This directory contains the source files for sendmail.
 
-For detailed instructions, please read the document ../doc/op.me:
-
-       eqn ../doc/op.me | pic | ditroff -me
+*********************
+!! DO NOT USE MAKE !!  to compile sendmail -- instead, use the
+*********************  "makesendmail" script located in the src
+directory.  It will find an appropriate Makefile, and create an
+appropriate obj.* subdirectory so that multiplatform support
+works easily.
 
 The Makefile is for the new (4.4BSD) Berkeley make and uses syntax
 that is not recognized by older makes.  It also has assumptions
 about the 4.4 file system layout built in.  See below for details
 about other Makefiles.
 
-There is also a Makefile.dist which is much less clever, but works on
-the old traditional make.  You can use this using:
+If you are porting to a new architecture for which there is no existing
+Makefile, you might start with Makefile.dist.  This works on the old
+traditional make, but isn't customized for any particular architecture.
 
-       make -f Makefile.dist
-
-**************************************************
-**  Read below for more details of Makefiles.  **
-**************************************************
-
-There is also a shell script (makesendmail) that tries to be clever
-about using object subdirectories.  It's pretty straightforward, and
-may help if you share a source tree among different architectures.
+       **************************************************
+       **  Read below for more details of Makefiles.   **
+       **************************************************
 
 **************************************************************************
 **  IMPORTANT:  DO NOT USE OPTIMIZATION (``-O'') IF YOU ARE RUNNING    **
@@ -67,16 +65,34 @@ 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.
+
 **************************************************************************
 **  IMPORTANT:  Read the appropriate paragraphs in the section on      **
 **  ``Operating System and Compile Quirks''.                           **
 **************************************************************************
 
+For detailed instructions, please read the document ../doc/op.me:
+
+       eqn ../doc/op.me | pic | ditroff -me
+
 
 +-----------+
 | MAKEFILES |
 +-----------+
 
+By far, the easiest way to compile sendmail is to use the "makesendmail"
+script:
+
+       sh makesendmail
+
+This uses the "uname" command to figure out what architecture you are
+on and selects 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.  However, if for some
+reason this doesn't work (e.g., NeXT systems don't have the "uname"
+command) you may have to set up your compile environment by hand.
+
 The "Makefile"s in these directories are from 4.4 BSD, and hence
 really only work properly if you are on a 4.4 system.  In particular,
 they use new syntax that will not be recognized on old make programs,
@@ -88,17 +104,15 @@ outside of the sendmail tree.
 Instead, you should use one of the other Makefiles, such as
 Makefile.SunOS for a SunOS system, and so forth.  These should
 work with the version of make that is appropriate for that
-system.
-
-There are a bunch of other Makefiles for other systems with names
-like Makefile.HPUX for an HP-UX system.  They use the version of
-make that is native for that system.  These are the Makefiles that
-I use, and they have "Berkeley quirks" in them.  I can't guarantee
-that they will work unmodified in your environment.  Many of them
-include -I/usr/sww/include/db and -L/usr/sww/lib -- this is Berkeley's
-location (the ``Software Warehouse'') for the new database libraries,
-described below.  You don't have to remove these definitions if you
-don't have these directories.
+system.  All other Makefiles are in the "src/Makefiles" subdirectory.
+They use the version of make that is native for that system.  These
+are the Makefiles that I use, and they have "Berkeley quirks" in them.
+I can't guarantee that they will work unmodified in your environment.
+In particular, Many of them include -I/usr/sww/include/db and
+-L/usr/sww/lib -- these are Berkeley's locations in the ``Software
+Warehouse'' for the new database libraries, described below.  You don't
+have to remove these definitions if you don't have these directories,
+but you may have to remove -DNEWDB from the DBMDEF definition.
 
 Please look for an appropriate Makefile before you start trying to
 compile with Makefile or Makefile.dist.
@@ -107,9 +121,10 @@ If you want to port the new Berkeley make, you can get it from
 ftp.uu.net in the directory /systems/unix/bsd-sources/usr.bin/make.
 Diffs and instructions for building this version of make under
 SunOS 4.1.x are available on ftp.css.itd.umich.edu in
-/pub/systems/sun/Net2-make.sun4.diff.Z.  Diffs and instructions
+/pub/systems/sun/Net2-make-sun4.diff.Z.  Diffs and instructions
 for building this version of make under IBM AIX 3.2.4 are available
 on ftp.uni-stuttgart.de in /sw/src/patches/bsd-make-rus-patches.
+For Ultrix, try ftp.vix.com:~ftp/pub/patches/pmake-for-ultrix.Z.
 Paul Southworth <pauls@umich.edu> published a description of porting
 this make in comp.unix.bsd.
 
@@ -133,10 +148,11 @@ The three options are NEWDB (the new Berkeley DB package), NDBM (the
 older DBM implementation -- the very old V7 implementation is no
 longer supported), and NIS (Network Information Services).  Used alone
 these just include the support they indicate.  [If you are using NEWDB,
-get the latest version from FTP.CS.Berkeley.EDU in /ucb/4bsd.  DO NOT
-use the version from the Net2 distribution!  However, if you are on
-BSD/386 or 386BSD-based systems, use the one that already exists
-on your system.  You may need to #define OLD_NEWDB 1 to do this.]
+get the latest version from FTP.CS.Berkeley.EDU in /ucb/4bsd/db.tar.Z
+(or db.tar.gz).  DO NOT use the version from the Net2 distribution!
+However, if you are on BSD/386 or 386BSD-based systems, use the one
+that already exists on your system.  You may need to #define OLD_NEWDB
+1 to do this.]
 
 [NOTE WELL: it is CRITICAL that you remove ndbm.o from libdb.a and
 ndbm.h from the appropriate include directories if you want to get
@@ -152,19 +168,28 @@ the NEWDB library also catches and maps NDBM calls; you will have to
 back out this feature to get this to work.  See ``Quirks'' section
 below for details.]
 
-If all three are defined, sendmail operates as described above, and also
-looks for the file /var/yp/Makefile.  If it exists, newaliases will
-build BOTH the NEWDB and NDBM format alias files.  However, it will
-only use the NEWDB file; the NDBM format file is used only by the
-NIS subsystem.
+If all three are 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.
 
-If NDBM and NIS are defined (regardless of the definition of NEWDB
-or the existance of /var/yp/Makefile), sendmail adds the special
+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 -DNEWDB, -DNDBM, and -DNIS are normally defined in the DBMDEF
-line in the Makefile.
+There is also preliminary support for NIS+ (-DNISPLUS), Hesiod
+(-DHESIOD), and NetInfo (-DNETINFO).  These have not been well
+tested.
+
+All of -DNEWDB, -DNDBM, -DNIS, -DNISPLUS, -DHESIOD, and -DNETINFO 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.
 
 
 +---------------+
@@ -174,25 +199,14 @@ line in the Makefile.
 Whereever 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 availble, requiring the following compilation flags in the
-Makefile:
-
-SOLARIS                Define this if you are running Solaris 2.0 or higher.
-SOLARIS_2_3    Define this if you are running Solaris 2.3 or higher.
-SUNOS403       Define this if you are running SunOS 4.0.3.
-NeXT           Define this if you are on a NeXT box.  (This one may
-               be pre-defined for you.)  There are other hacks you
-               have to make -- see below.
-_AIX3          Define this if you are IBM AIX 3.x.
-RISCOS         Define this if you are running RISC/os from MIPS.
-IRIX           Define this if you are running IRIX from SGI.
-_SCO_unix_     Define this if you are on SCO UNIX.
-_SCO_unix_4_2  Define this if you are on SCO Open Server 3.2v4.
-
-If you are a system that sendmail has already been ported to, you
-probably won't have to touch these.  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:
+symbols available, requiring that a compilation flag be defined in
+the Makefile; see the Makefiles 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
@@ -201,12 +215,18 @@ SYS5SIGNALS       Use System V signal semantics -- the signal handler
                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.
-               For this reason, this should not be set unless you
-               don't have an alternative.
+               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"
@@ -234,11 +254,28 @@ HASSETREUID       Define this if you have setreuid(2) ***AND*** root can
                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 seteuid(2) if you have a seteuid
+               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.
 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
@@ -261,6 +298,12 @@ HASGETUSERSHELL    Define this to 1 if you have getusershell(3) in your
                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
@@ -276,35 +319,71 @@ ARBPTR_T  The type of an arbitrary pointer -- defaults to "void *".
                this to be "char *".
 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
+                LA_ZERO (1) -- it always returns the load average as
                        "zero" (and does so on all architectures).
-               LA_SUBR (4) if you have the getloadavg(3) routine,
-               LA_MACH (5) to use MACH-style load averages (calls
+                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
+                LA_PROCSTR (7) to read /proc/loadavg and interpret it
                        as a string representing a floating-point
-                       number (Linux-style),
-               LA_FLOAT (3) if you read kmem and interpret the value
-                       as a floating point number,
-               LA_INT (2) to interpret as a long integer,
-               LA_SHORT (6) to interpret as a short integer.
-               These last three 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.
+                       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_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.
                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>),
-               and SFS_VFS (3), SFS_MOUNT (4), SFS_STATFS (5) or
-               SFS_STATVFS (6) if you have the two-argument statfs(2)
-               system call, with includes in <sys/vfs.h>, <sys/mount.h>,
-               <sys/statfs.h>, or <sys/statvfs.h> respectively.  The
-               default if nothing is defined is SFS_NONE.
+               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 hou 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_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
@@ -329,6 +408,12 @@ BROKEN_RES_SEARCH
                -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.
+
 
 
 +-----------------------+
@@ -351,24 +436,50 @@ OLD_NEWDB If non-zero, the version of NEWDB you have is the old
                use -DOLD_NEWDB=0 it forces you to use the new interface.
 NIS            Define this to get NIS (YP) support for aliases and maps.
                Normally defined in the Makefile.
-USERDB         Include support for the User Information Database.  Implied
-               by NEWDB in conf.h.
+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.
-MIME           Include support for MIME-encapsulated error messages.
+               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 AIX 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     Define this to get DNS (name daemon) support, including
-               MX support.  The specs you must use this if you run
-               SMTP.  Defined by default in conf.h.
+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.
@@ -379,9 +490,16 @@ 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.
-SETPROCTITLE   Try to set the string printed by "ps" to something
-               informative about what sendmail is doing.  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.  Not yet
+               implemented.
+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.
 
 
 +---------------------+
@@ -405,6 +523,13 @@ 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 |
@@ -431,7 +556,7 @@ GCC 2.5.x problems  *** IMPORTANT ***
        *************** find_reloads_toplev (x, opnum, type, ind
        *** 3888,3894 ****
                 force a reload in that case.  So we should not do anything here.  */
-         
+
                else if (regno >= FIRST_PSEUDO_REGISTER
        ! #if defined(BYTE_LOADS_ZERO_EXTEND) || defined(BYTE_LOADS_SIGN_EXTEND)
                       && (GET_MODE_SIZE (GET_MODE (x))
@@ -439,13 +564,28 @@ GCC 2.5.x problems  *** IMPORTANT ***
          #endif
        --- 3888,3894 ----
                 force a reload in that case.  So we should not do anything here.  */
-         
+
                else if (regno >= FIRST_PSEUDO_REGISTER
        ! #ifdef LOAD_EXTEND_OP
                       && (GET_MODE_SIZE (GET_MODE (x))
                           <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
          #endif
 
+GCC 2.7.x problems
+       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.
+
+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.
+       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
@@ -469,6 +609,40 @@ SunOS 4.x (Solaris 1.x)
        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 the
+         Makefile.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_U1
+       Sendmail causes crashes on SunOS 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.
+
+       This is fixed in patch 101790-01 (SunOS 4.1.3_U1: TCP socket and
+       reset problems).
+
 Solaris 2.x (SunOS 5.x)
        To compile for Solaris, be sure you use -DSOLARIS.
 
@@ -478,7 +652,7 @@ Solaris 2.x (SunOS 5.x)
 
        From a correspondent:
 
-          For solaris 2.2, I have 
+          For solaris 2.2, I have
 
                hosts:      files dns
 
@@ -522,8 +696,54 @@ Solaris 2.x (SunOS 5.x)
        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.
+
+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.
+
 OSF/1
-       If you are compiling on OSF/1 (DEC Alpha), you must use 
+       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.
@@ -542,9 +762,17 @@ IRIX
        when compiling map.c; this is not important because the
        function being prototyped is not used in that file.
 
-NeXT
-       If you are compiling on NeXT, you will have to create an empty
-       file "unistd.h" and create a file "dirent.h" containing:
+       In order to compile sendmail you will have had to install
+       the developers' option in order to get the necessary include
+       files.
+
+NeXT or NEXTSTEP
+       NEXTSTEP 3.3 and earlier ship with the old DBM library.  You will
+       need to acquire the new Berkeley DB from ftp.cs.berkeley.edu.
+       Install it in /usr/local/{lib,include}.
+
+       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
@@ -626,10 +854,29 @@ A/UX
        (not easy at least); the gnu-dbm package "configured" and
        compiled easily.
 
+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 -
+
 DG/UX
-       Apparently, /bin/mail doesn't work properly for delivery on
-       DG/UX -- the person who has this working, Douglas Anderson
-       <dlander@afterlife.ncsc.mil>, used procmail instead.
+       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
@@ -677,10 +924,22 @@ Linux
        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).
+
 AIX
        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/.
+
 RISC/os
        RISC/os from MIPS is a merged AT&T/Berkeley system.  When you
        compile on that platform you will get duplicate definitions
@@ -727,7 +986,7 @@ DELL SVR4
           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. 
+       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
@@ -738,11 +997,32 @@ DELL SVR4
 
        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 2.0
+       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.
+
+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>.
 
 Non-DNS based sites
        This version of sendmail always tries to connect to the Domain
@@ -769,9 +1049,10 @@ GNU getopt
        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 the conf/Info.Ultrix
-       carefully -- there is information in there that you need to know
-       in order to avoid errors of the form:
+       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
@@ -780,6 +1061,35 @@ BIND 4.9.2 and Ultrix
 
        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. :)
+
+
 
 +--------------+
 | MANUAL PAGES |
@@ -880,4 +1190,4 @@ version.c  The version number and information about this
 
 Eric Allman
 
-(Version 8.61.1.1, last update 3/5/95 12:52:16)
+(Version 8.113, last update 11/29/95 11:05:14)
index f05c219..f2499aa 100644 (file)
@@ -4,7 +4,7 @@
 0, 44  util.c          printav print address of each string
 1      main.c          main    print from person
 2      main.c          finis
-3      conf.c          getla
+3      conf.c          getla, shouldqueue
 4      conf.c          enoughspace
 5      clock.c         setevent, clrevent, tick
 6      savemail.c      savemail, returntosender
@@ -50,6 +50,9 @@
 40     queue.c         queueup, orderq, dowork
 41     queue.c         orderq
 42     mci.c           mci_get
+43     mime.c          mime8to7
+44     recipient.c     writeable
+44     util.c          safefile
 45     envelope.c      setsender
 46     envelope.c      openxscript
 49     conf.c          checkcompat
@@ -61,4 +64,8 @@
 55     conf.c          lockfile
 59     Extended Load Average implementation from Christophe Wolfhugel
 60     map.c
+61     conf.c          sm_gethostbyname
+80                     content length
+81                     sun remote mode
 91     mci.c           syslogging of MCI cache information
+99     main.c          avoid backgrounding (no printed output)
index 9952db5..2bc4487 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 # include "sendmail.h"
-# include <pwd.h>
 
 #ifndef lint
-static char sccsid[] = "@(#)alias.c    8.25 (Berkeley) 4/14/94";
+static char sccsid[] = "@(#)alias.c    8.52 (Berkeley) 10/28/95";
 #endif /* not lint */
 
 
-MAP    *AliasDB[MAXALIASDB + 1];       /* actual database list */
-int    NAliasDBs;                      /* number of alias databases */
+MAP    *AliasFileMap = NULL;   /* the actual aliases.files map */
+int    NAliasFileMaps;         /* the number of entries in AliasFileMap */
 \f/*
 **  ALIAS -- Compute aliases.
 **
@@ -53,6 +52,7 @@ int   NAliasDBs;                      /* number of alias databases */
 **             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:
@@ -66,19 +66,22 @@ int NAliasDBs;                      /* number of alias databases */
 **                     nothing.
 */
 
-alias(a, sendq, e)
+void
+alias(a, sendq, aliaslevel, e)
        register ADDRESS *a;
        ADDRESS **sendq;
+       int aliaslevel;
        register ENVELOPE *e;
 {
        register char *p;
        int naliases;
        char *owner;
+       auto int stat = EX_OK;
        char obuf[MAXNAME + 6];
        extern char *aliaslookup();
 
        if (tTd(27, 1))
-               printf("alias(%s)\n", a->q_paddr);
+               printf("alias(%s)\n", a->q_user);
 
        /* don't realias already aliased names */
        if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
@@ -90,10 +93,21 @@ alias(a, sendq, e)
        e->e_to = a->q_paddr;
 
        /*
-       **  Look up this name
+       **  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, e);
+       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 = "alias database unavailable";
+               return;
+       }
        if (p == NULL)
                return;
 
@@ -108,29 +122,25 @@ alias(a, sendq, e)
        if (bitset(EF_VRFYONLY, e->e_flags))
        {
                a->q_flags |= QVERIFIED;
-               e->e_nrcpts++;
                return;
        }
-       message("aliased to %s", p);
+       message("aliased to %s", shortenstring(p, 203));
 #ifdef LOG
        if (LogLevel > 9)
-               syslog(LOG_INFO, "%s: alias %s => %s",
+               syslog(LOG_INFO, "%s: alias %.100s => %s",
                        e->e_id == NULL ? "NOQUEUE" : e->e_id,
-                       a->q_paddr, p);
+                       a->q_paddr, shortenstring(p, 203));
 #endif
        a->q_flags &= ~QSELFREF;
-       AliasLevel++;
-       naliases = sendtolist(p, a, sendq, e);
-       AliasLevel--;
-       if (!bitset(QSELFREF, a->q_flags))
+       if (tTd(27, 5))
        {
-               if (tTd(27, 5))
-               {
-                       printf("alias: QDONTSEND ");
-                       printaddr(a, FALSE);
-               }
-               a->q_flags |= QDONTSEND;
+               printf("alias: QDONTSEND ");
+               printaddr(a, FALSE);
        }
+       a->q_flags |= QDONTSEND;
+       naliases = sendtolist(p, a, sendq, aliaslevel + 1, e);
+       if (bitset(QSELFREF, a->q_flags))
+               a->q_flags &= ~QDONTSEND;
 
        /*
        **  Look for owner of alias
@@ -143,7 +153,7 @@ alias(a, sendq, e)
                (void) strcat(obuf, a->q_user);
        if (!bitnset(M_USR_UPPER, a->q_mailer->m_flags))
                makelower(obuf);
-       owner = aliaslookup(obuf, e);
+       owner = aliaslookup(obuf, &stat, e);
        if (owner == NULL)
                return;
 
@@ -154,17 +164,18 @@ alias(a, sendq, e)
 
        /* 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;
-       }
+       e->e_flags |= EF_SENDRECEIPT;
+       a->q_flags |= QDELIVERED|QEXPANDED;
 }
 \f/*
 **  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.
@@ -178,26 +189,24 @@ alias(a, sendq, e)
 */
 
 char *
-aliaslookup(name, e)
+aliaslookup(name, pstat, e)
        char *name;
+       int *pstat;
        ENVELOPE *e;
 {
-       register int dbno;
-       register MAP *map;
-       register char *p;
+       static MAP *map = NULL;
 
-       for (dbno = 0; dbno < NAliasDBs; dbno++)
+       if (map == NULL)
        {
-               auto int stat;
+               STAB *s = stab("aliases", ST_MAP, ST_FIND);
 
-               map = AliasDB[dbno];
-               if (!bitset(MF_OPEN, map->map_mflags))
-                       continue;
-               p = (*map->map_class->map_lookup)(map, name, NULL, &stat);
-               if (p != NULL)
-                       return p;
+               if (s == NULL)
+                       return NULL;
+               map = &s->s_map;
        }
-       return NULL;
+       if (!bitset(MF_OPEN, map->map_mflags))
+               return NULL;
+       return (*map->map_class->map_lookup)(map, name, NULL, pstat);
 }
 \f/*
 **  SETALIAS -- set up an alias map
@@ -211,6 +220,7 @@ aliaslookup(name, e)
 **             none.
 */
 
+void
 setalias(spec)
        char *spec;
 {
@@ -224,7 +234,7 @@ setalias(spec)
 
        for (p = spec; p != NULL; )
        {
-               char aname[50];
+               char buf[50];
 
                while (isspace(*p))
                        p++;
@@ -232,16 +242,27 @@ setalias(spec)
                        break;
                spec = p;
 
-               if (NAliasDBs >= MAXALIASDB)
+               if (NAliasFileMaps >= MAXMAPSTACK)
                {
-                       syserr("Too many alias databases defined, %d max", MAXALIASDB);
+                       syserr("Too many alias databases defined, %d max",
+                               MAXMAPSTACK);
                        return;
                }
-               (void) sprintf(aname, "Alias%d", NAliasDBs);
-               s = stab(aname, ST_MAP, ST_ENTER);
+               if (AliasFileMap == NULL)
+               {
+                       strcpy(buf, "aliases.files sequence");
+                       AliasFileMap = makemapentry(buf);
+                       if (AliasFileMap == NULL)
+                       {
+                               syserr("setalias: cannot create aliases.files map");
+                               return;
+                       }
+               }
+               (void) sprintf(buf, "Alias%d", NAliasFileMaps);
+               s = stab(buf, ST_MAP, ST_ENTER);
                map = &s->s_map;
-               AliasDB[NAliasDBs] = map;
                bzero(map, sizeof *map);
+               map->map_mname = s->s_name;
 
                p = strpbrk(p, " ,/:");
                if (p != NULL && *p == ':')
@@ -263,6 +284,9 @@ setalias(spec)
                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)
@@ -281,7 +305,7 @@ setalias(spec)
                        if (map->map_class->map_parse(map, spec))
                        {
                                map->map_mflags |= MF_VALID|MF_ALIAS;
-                               NAliasDBs++;
+                               AliasFileMap->map_stack[NAliasFileMaps++] = map;
                        }
                }
        }
@@ -313,7 +337,7 @@ aliaswait(map, ext, isopen)
        bool attimeout = FALSE;
        time_t mtime;
        struct stat stb;
-       char buf[MAXNAME];
+       char buf[MAXNAME + 1];
 
        if (tTd(27, 3))
                printf("aliaswait(%s:%s)\n",
@@ -348,6 +372,7 @@ aliaswait(map, ext, isopen)
                                        sleeptime);
 
                        map->map_class->map_close(map);
+                       map->map_mflags &= ~(MF_OPEN|MF_WRITABLE);
                        sleep(sleeptime);
                        sleeptime *= 2;
                        if (sleeptime > 60)
@@ -380,11 +405,19 @@ aliaswait(map, ext, isopen)
                /* database is out of date */
                if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
                {
+                       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);
+                       }
                        rebuildaliases(map, TRUE);
                        isopen = map->map_class->map_open(map, O_RDONLY);
+                       SuprErrs = oldSuprErrs;
                }
                else
                {
@@ -414,13 +447,17 @@ aliaswait(map, ext, isopen)
 **             DBM or DB version.
 */
 
+void
 rebuildaliases(map, automatic)
        register MAP *map;
        bool automatic;
 {
        FILE *af;
        bool nolock = FALSE;
-       sigfunc_t oldsigint;
+       sigfunc_t oldsigint, oldsigquit;
+#ifdef SIGTSTP
+       sigfunc_t oldsigtstp;
+#endif
 
        if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
                return;
@@ -428,6 +465,8 @@ rebuildaliases(map, automatic)
        /* try to lock the source file */
        if ((af = fopen(map->map_file, "r+")) == NULL)
        {
+               struct stat stb;
+
                if ((errno != EACCES && errno != EROFS) || automatic ||
                    (af = fopen(map->map_file, "r")) == NULL)
                {
@@ -436,15 +475,18 @@ rebuildaliases(map, automatic)
                        if (tTd(27, 1))
                                printf("Can't open %s: %s\n",
                                        map->map_file, errstring(saveerr));
-                       if (!automatic)
+                       if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
                                message("newaliases: cannot open %s: %s",
                                        map->map_file, errstring(saveerr));
                        errno = 0;
                        return;
                }
                nolock = TRUE;
-               message("warning: cannot lock %s: %s",
-                       map->map_file, errstring(errno));
+               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 */
@@ -465,7 +507,13 @@ rebuildaliases(map, automatic)
                return;
        }
 
+       /* avoid denial-of-service attacks */
+       resetlimits();
        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))
        {
@@ -478,7 +526,7 @@ rebuildaliases(map, automatic)
                }
 #endif /* LOG */
                map->map_mflags |= MF_OPEN|MF_WRITABLE;
-               readaliases(map, af, automatic);
+               readaliases(map, af, !automatic, TRUE);
        }
        else
        {
@@ -495,10 +543,17 @@ rebuildaliases(map, automatic)
 
        /* 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 signal */
+       /* restore the old signals */
        (void) setsignal(SIGINT, oldsigint);
+       (void) setsignal(SIGQUIT, oldsigquit);
+#ifdef SIGTSTP
+       (void) setsignal(SIGTSTP, oldsigtstp);
+#endif
 }
 \f/*
 **  READALIASES -- read and process the alias file.
@@ -509,7 +564,9 @@ rebuildaliases(map, automatic)
 **     Parameters:
 **             map -- the alias database descriptor.
 **             af -- file to read the aliases from.
-**             automatic -- set if this was an automatic rebuild.
+**             announcestats -- anounce statistics regarding number of
+**                     aliases, longest alias, etc.
+**             logstats -- lot the same info.
 **
 **     Returns:
 **             none.
@@ -519,10 +576,12 @@ rebuildaliases(map, automatic)
 **             Optionally, builds the .dir & .pag files.
 */
 
-readaliases(map, af, automatic)
+void
+readaliases(map, af, announcestats, logstats)
        register MAP *map;
        FILE *af;
-       int automatic;
+       bool announcestats;
+       bool logstats;
 {
        register char *p;
        char *rhs;
@@ -627,7 +686,7 @@ readaliases(map, af, automatic)
                        }
 
                        /* see if there should be a continuation line */
-                       c = fgetc(af);
+                       c = getc(af);
                        if (!feof(af))
                                (void) ungetc(c, af);
                        if (c != ' ' && c != '\t')
@@ -645,7 +704,7 @@ readaliases(map, af, automatic)
                                break;
                        }
                }
-               if (al.q_mailer != LocalMailer)
+               if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
                {
                        syserr("554 %s... cannot alias non-local names",
                                al.q_paddr);
@@ -679,11 +738,11 @@ readaliases(map, af, automatic)
 
        CurEnv->e_to = NULL;
        FileName = NULL;
-       if (Verbose || !automatic)
+       if (Verbose || announcestats)
                message("%s: %d aliases, longest %d bytes, %d bytes total",
                        map->map_file, naliases, longest, bytes);
 # ifdef LOG
-       if (LogLevel > 7)
+       if (LogLevel > 7 && logstats)
                syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total",
                        map->map_file, naliases, longest, bytes);
 # endif /* LOG */
@@ -700,6 +759,8 @@ readaliases(map, af, automatic)
 **                     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.
@@ -708,9 +769,11 @@ readaliases(map, af, automatic)
 **             New names are added to send queues.
 */
 
-forward(user, sendq, e)
+void
+forward(user, sendq, aliaslevel, e)
        ADDRESS *user;
        ADDRESS **sendq;
+       int aliaslevel;
        register ENVELOPE *e;
 {
        char *pp;
@@ -719,7 +782,8 @@ forward(user, sendq, e)
        if (tTd(27, 1))
                printf("forward(%s)\n", user->q_paddr);
 
-       if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
+       if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
+           bitset(QBADADDR, user->q_flags))
                return;
        if (user->q_home == NULL)
        {
@@ -738,17 +802,18 @@ forward(user, sendq, e)
        {
                int err;
                char buf[MAXPATHLEN+1];
+               extern int include();
 
                ep = strchr(pp, ':');
                if (ep != NULL)
                        *ep = '\0';
-               expand(pp, buf, &buf[sizeof buf - 1], e);
+               expand(pp, buf, sizeof buf, e);
                if (ep != NULL)
                        *ep++ = ':';
                if (tTd(27, 3))
                        printf("forward: trying %s\n", buf);
 
-               err = include(buf, TRUE, user, sendq, e);
+               err = include(buf, TRUE, user, sendq, aliaslevel, e);
                if (err == 0)
                        break;
                else if (transienterror(err))
index d3f9ac5..f676470 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)arpadate.c 8.1 (Berkeley) 6/7/93";
+static char sccsid[] = "@(#)arpadate.c 8.4 (Berkeley) 4/21/95";
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -64,6 +64,17 @@ static char sccsid[] = "@(#)arpadate.c       8.1 (Berkeley) 6/7/93";
 **             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;
@@ -75,7 +86,8 @@ arpadate(ud)
        register struct tm *lt;
        time_t t;
        struct tm gmt;
-       static char b[40];
+       char *tz;
+       static char b[43 + TZNAME_MAX];
 
        /*
        **  Get current time.
@@ -147,15 +159,41 @@ arpadate(ud)
                off += 24 * 60;
 
        *q++ = ' ';
-       if (off == 0) {
+       if (off == 0)
+       {
                *q++ = 'G';
                *q++ = 'M';
                *q++ = 'T';
-       } else {
-               if (off < 0) {
+       }
+       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
+               }
+               else
                        *q++ = '+';
 
                if (off >= 24*60)               /* should be impossible */
@@ -166,6 +204,14 @@ arpadate(ud)
                off %= 60;
                *q++ = (off / 10) + '0';
                *q++ = (off % 10) + '0';
+               if (tz != NULL && *tz != '\0')
+               {
+                       *q++ = ' ';
+                       *q++ = '(';
+                       while (*tz != '\0')
+                               *q++ = *tz++;
+                       *q++ = ')';
+               }
        }
        *q = '\0';
 
index 45ef1c2..1490137 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)clock.c    8.8 (Berkeley) 1/12/94";
+static char sccsid[] = "@(#)clock.c    8.12 (Berkeley) 5/23/95";
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -65,7 +65,7 @@ static void tick __P((int));
 EVENT *
 setevent(intvl, func, arg)
        time_t intvl;
-       int (*func)();
+       void (*func)();
        int arg;
 {
        register EVENT **evp;
@@ -117,6 +117,7 @@ setevent(intvl, func, arg)
 **             arranges for event ev to not happen.
 */
 
+void
 clrevent(ev)
        register EVENT *ev;
 {
@@ -182,7 +183,7 @@ tick(arg)
        while ((ev = EventQueue) != NULL &&
               (ev->ev_time <= now || ev->ev_pid != mypid))
        {
-               int (*f)();
+               void (*f)();
                int arg;
                int pid;
 
@@ -216,10 +217,10 @@ tick(arg)
                sigaddset(&ss, SIGALRM);
                sigprocmask(SIG_UNBLOCK, &ss, NULL);
 #else
-#ifdef SIGVTALRM
+#if HASSIGSETMASK
                /* reset 4.2bsd signal mask to allow future alarms */
                (void) sigsetmask(sigblock(0) & ~sigmask(SIGALRM));
-#endif /* SIGVTALRM */
+#endif /* HASSIGSETMASK */
 #endif /* SIG_UNBLOCK */
 
                /* call ev_func */
@@ -251,7 +252,7 @@ tick(arg)
 */
 
 static bool    SleepDone;
-static int     endsleep();
+static void    endsleep();
 
 #ifndef SLEEP_T
 # define SLEEP_T       unsigned int
@@ -262,14 +263,15 @@ sleep(intvl)
        unsigned int intvl;
 {
        if (intvl == 0)
-               return;
+               return (SLEEP_T) 0;
        SleepDone = FALSE;
        (void) setevent((time_t) intvl, endsleep, 0);
        while (!SleepDone)
                pause();
+       return (SLEEP_T) 0;
 }
 
-static
+static void
 endsleep()
 {
        SleepDone = TRUE;
index b77f6e9..7e43c5f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)collect.c  8.14 (Berkeley) 4/18/94";
+static char sccsid[] = "@(#)collect.c  8.49 (Berkeley) 10/29/95";
 #endif /* not lint */
 
 # include <errno.h>
@@ -47,12 +47,14 @@ static char sccsid[] = "@(#)collect.c       8.14 (Berkeley) 4/18/94";
 **     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.
 **             requeueflag -- this message will be requeued later, so
 **                     don't do final processing on it.
+**             hdrp -- the location to stash the header.
 **             e -- the current envelope.
 **
 **     Returns:
@@ -63,36 +65,77 @@ static char sccsid[] = "@(#)collect.c       8.14 (Berkeley) 4/18/94";
 **             The from person may be set.
 */
 
-char   *CollectErrorMessage;
-bool   CollectErrno;
-
-collect(smtpmode, requeueflag, e)
+static jmp_buf CtxCollectTimeout;
+static void    collecttimeout();
+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, requeueflag, hdrp, e)
+       FILE *fp;
        bool smtpmode;
        bool requeueflag;
+       HDR **hdrp;
        register ENVELOPE *e;
 {
        register FILE *tf;
        bool ignrdot = smtpmode ? FALSE : IgnrDot;
-       char buf[MAXLINE], buf2[MAXLINE];
-       register char *workbuf, *freebuf;
+       time_t dbto = smtpmode ? TimeOuts.to_datablock : 0;
+       register char *bp;
+       int c = '\0';
        bool inputerr = FALSE;
-       extern char *hvalue();
-       extern bool isheader(), flusheol();
-
-       CollectErrorMessage = NULL;
-       CollectErrno = 0;
+       bool headeronly;
+       char *buf;
+       int buflen;
+       int istate;
+       int mstate;
+       char *pbp;
+       char peekbuf[8];
+       char dfname[20];
+       char bufbuf[MAXLINE];
+       extern bool isheader();
+       extern void eatheader();
+       extern void tferror();
+
+       headeronly = hdrp != NULL;
 
        /*
        **  Create the temp file name and create the file.
        */
 
-       e->e_df = queuename(e, 'd');
-       e->e_df = newstr(e->e_df);
-       if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL)
+       if (!headeronly)
        {
-               syserr("Cannot create %s", e->e_df);
-               NoReturn = TRUE;
-               finis();
+               struct stat stbuf;
+
+               strcpy(dfname, queuename(e, 'd'));
+               if ((tf = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == 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;
        }
 
        /*
@@ -102,224 +145,296 @@ collect(smtpmode, requeueflag, e)
        if (smtpmode)
                message("354 Enter mail, end with \".\" on a line by itself");
 
-       /* set global timer to monitor progress */
-       sfgetset(TimeOuts.to_datablock);
+       if (tTd(30, 2))
+               printf("collect\n");
 
        /*
-       **  Try to read a UNIX-style From line
+       **  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).
        */
 
-       if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
-                       "initial message read") == NULL)
-               goto readerr;
-       fixcrlf(buf, FALSE);
-# ifndef NOTUNIX
-       if (!SaveFrom && strncmp(buf, "From ", 5) == 0)
+       buf = bp = bufbuf;
+       buflen = sizeof bufbuf;
+       pbp = peekbuf;
+       istate = IS_BOL;
+       mstate = SaveFrom ? MS_HEADER : MS_UFROM;
+       CollectProgress = FALSE;
+
+       if (dbto != 0)
        {
-               if (!flusheol(buf, InChannel))
-                       goto readerr;
-               eatfrom(buf, e);
-               if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
-                               "message header read") == NULL)
+               /* handle possible input timeout */
+               if (setjmp(CtxCollectTimeout) != 0)
+               {
+#ifdef LOG
+                       syslog(LOG_NOTICE,
+                           "timeout waiting for input from %s during message collect",
+                           CurHostName ? CurHostName : "<local machine>");
+#endif
+                       errno = 0;
+                       usrerr("451 timeout waiting for input during message collect");
                        goto readerr;
-               fixcrlf(buf, FALSE);
+               }
+               CollectTimeout = setevent(dbto, collecttimeout, dbto);
        }
-# endif /* NOTUNIX */
 
-       /*
-       **  Copy InChannel to temp file & do message editing.
-       **      To keep certain mailers from getting confused,
-       **      and to keep the output clean, lines that look
-       **      like UNIX "From" lines are deleted in the header.
-       */
-
-       workbuf = buf;          /* `workbuf' contains a header field */
-       freebuf = buf2;         /* `freebuf' can be used for read-ahead */
        for (;;)
        {
-               char *curbuf;
-               int curbuffree;
-               register int curbuflen;
-               char *p;
-
-               /* first, see if the header is over */
-               if (!isheader(workbuf))
-               {
-                       fixcrlf(workbuf, TRUE);
-                       break;
-               }
-
-               /* if the line is too long, throw the rest away */
-               if (!flusheol(workbuf, InChannel))
-                       goto readerr;
-
-               /* it's okay to toss '\n' now (flusheol() needed it) */
-               fixcrlf(workbuf, TRUE);
-
-               curbuf = workbuf;
-               curbuflen = strlen(curbuf);
-               curbuffree = MAXLINE - curbuflen;
-               p = curbuf + curbuflen;
-
-               /* get the rest of this field */
+               if (tTd(30, 35))
+                       printf("top, istate=%d, mstate=%d\n", istate, mstate);
                for (;;)
                {
-                       int clen;
-
-                       if (sfgets(freebuf, MAXLINE, InChannel,
-                                       TimeOuts.to_datablock,
-                                       "message header read") == NULL)
+                       if (pbp > peekbuf)
+                               c = *--pbp;
+                       else
                        {
-                               freebuf[0] = '\0';
-                               break;
+                               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 <<< ",
+                                                       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 (!headeronly)
+                                       e->e_msgsize++;
                        }
+                       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;
 
-                       /* is this a continuation line? */
-                       if (*freebuf != ' ' && *freebuf != '\t')
+                         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;
 
-                       if (!flusheol(freebuf, InChannel))
-                               goto readerr;
+                         case IS_DOTCR:
+                               if (c == '\n')
+                                       goto readerr;
+                               else
+                               {
+                                       /* push back the ".\rx" */
+                                       *pbp++ = c;
+                                       *pbp++ = '\r';
+                                       c = '.';
+                               }
+                               break;
 
-                       fixcrlf(freebuf, TRUE);
-                       clen = strlen(freebuf) + 1;
+                         case IS_CR:
+                               if (c == '\n')
+                                       istate = IS_BOL;
+                               else
+                               {
+                                       ungetc(c, fp);
+                                       c = '\r';
+                                       istate = IS_NORM;
+                               }
+                               goto bufferchar;
+                       }
 
-                       /* if insufficient room, dynamically allocate buffer */
-                       if (clen >= curbuffree)
+                       if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags))
                        {
-                               /* reallocate buffer */
-                               int nbuflen = ((p - curbuf) + clen) * 2;
-                               char *nbuf = xalloc(nbuflen);
-
-                               p = nbuf + curbuflen;
-                               curbuffree = nbuflen - curbuflen;
-                               bcopy(curbuf, nbuf, curbuflen);
-                               if (curbuf != buf && curbuf != buf2)
-                                       free(curbuf);
-                               curbuf = nbuf;
+                               istate = IS_CR;
+                               continue;
                        }
-                       *p++ = '\n';
-                       bcopy(freebuf, p, clen - 1);
-                       p += clen - 1;
-                       curbuffree -= clen;
-                       curbuflen += clen;
-               }
-               *p++ = '\0';
-
-               e->e_msgsize += curbuflen;
-
-               /*
-               **  The working buffer now becomes the free buffer, since
-               **  the free buffer contains a new header field.
-               **
-               **  This is premature, since we still havent called
-               **  chompheader() to process the field we just created
-               **  (so the call to chompheader() will use `freebuf').
-               **  This convolution is necessary so that if we break out
-               **  of the loop due to H_EOH, `workbuf' will always be
-               **  the next unprocessed buffer.
-               */
+                       else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags))
+                               istate = IS_BOL;
+                       else
+                               istate = IS_NORM;
 
-               {
-                       register char *tmp = workbuf;
-                       workbuf = freebuf;
-                       freebuf = tmp;
-               }
+bufferchar:
+                       if (mstate == MS_BODY)
+                       {
+                               /* just put the character out */
+                               if (MaxMessageSize <= 0 ||
+                                   e->e_msgsize <= MaxMessageSize)
+                                       putc(c, tf);
+                               continue;
+                       }
 
-               /*
-               **  Snarf header away.
-               */
+                       /* 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 != '\0')
+                               *bp++ = c;
+                       if (istate == IS_BOL)
+                               break;
+               }
+               *bp = '\0';
 
-               if (bitset(H_EOH, chompheader(curbuf, FALSE, e)))
-                       break;
+nextstate:
+               if (tTd(30, 35))
+                       printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n",
+                               istate, mstate, buf);
+               switch (mstate)
+               {
+                       extern int chompheader();
 
-               /*
-               **  If the buffer was dynamically allocated, free it.
-               */
+                 case MS_UFROM:
+                       mstate = MS_HEADER;
+#ifndef NOTUNIX
+                       if (strncmp(buf, "From ", 5) == 0)
+                       {
+                               extern void eatfrom();
 
-               if (curbuf != buf && curbuf != buf2)
-                       free(curbuf);
-       }
+                               bp = buf;
+                               eatfrom(buf, e);
+                               continue;
+                       }
+#endif
+                       /* fall through */
 
-       if (tTd(30, 1))
-               printf("EOH\n");
+                 case MS_HEADER:
+                       if (!isheader(buf))
+                       {
+                               mstate = MS_BODY;
+                               goto nextstate;
+                       }
 
-       if (*workbuf == '\0')
-       {
-               /* throw away a blank line */
-               if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
-                               "message separator read") == NULL)
-                       goto readerr;
-       }
-       else if (workbuf == buf2)       /* guarantee `buf' contains data */
-               (void) strcpy(buf, buf2);
+                       /* 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;
+                       }
 
-       /*
-       **  Collect the body of the message.
-       */
+                       /* 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;
+                       break;
 
-       for (;;)
-       {
-               register char *bp = buf;
+                 case MS_BODY:
+                       if (tTd(30, 1))
+                               printf("EOH\n");
+                       if (headeronly)
+                               goto readerr;
+                       bp = buf;
 
-               fixcrlf(buf, TRUE);
+                       /* 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;
+                       }
 
-               /* check for end-of-message */
-               if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
+                       /* if not a blank separator, write it out */
+                       if (MaxMessageSize <= 0 ||
+                           e->e_msgsize <= MaxMessageSize)
+                       {
+                               while (*bp != '\0')
+                                       putc(*bp++, tf);
+                       }
                        break;
-
-               /* check for transparent dot */
-               if ((OpMode == MD_SMTP || OpMode == MD_DAEMON) &&
-                   bp[0] == '.' && bp[1] == '.')
-                       bp++;
-
-               /*
-               **  Figure message length, output the line to the temp
-               **  file, and insert a newline if missing.
-               */
-
-               e->e_msgsize += strlen(bp) + 1;
-               fputs(bp, tf);
-               fputs("\n", tf);
-               if (ferror(tf))
-                       tferror(tf, e);
-               if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
-                               "message body read") == NULL)
-                       goto readerr;
+               }
+               bp = buf;
        }
 
-       if (feof(InChannel) || ferror(InChannel))
-       {
 readerr:
+       if ((feof(fp) && smtpmode) || ferror(fp))
+       {
+               const char *errmsg = errstring(errno);
+
                if (tTd(30, 1))
-                       printf("collect: read error\n");
+                       printf("collect: premature EOM: %s\n", errmsg);
+#ifdef LOG
+               if (LogLevel >= 2)
+                       syslog(LOG_WARNING, "collect: premature EOM: %s", errmsg);
+#endif
                inputerr = TRUE;
        }
 
        /* reset global timer */
-       sfgetset((time_t) 0);
+       clrevent(CollectTimeout);
 
-       if (fflush(tf) != 0)
-               tferror(tf, e);
-       if (fsync(fileno(tf)) < 0 || fclose(tf) < 0)
+       if (headeronly)
+               return;
+
+       if (tf != NULL &&
+           (fflush(tf) != 0 || ferror(tf) || fsync(fileno(tf)) < 0 ||
+            fclose(tf) < 0))
        {
                tferror(tf, e);
+               flush_errors(TRUE);
                finis();
        }
 
-       if (CollectErrorMessage != NULL && Errors <= 0)
-       {
-               if (CollectErrno != 0)
-               {
-                       errno = CollectErrno;
-                       syserr(CollectErrorMessage, e->e_df);
-                       finis();
-               }
-               usrerr(CollectErrorMessage);
-       }
-       else if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
+       /* An EOF when running SMTP is an error */
+       if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
        {
-               /* An EOF when running SMTP is an error */
                char *host;
                char *problem;
 
@@ -327,24 +442,28 @@ readerr:
                if (host == NULL)
                        host = "localhost";
 
-               if (feof(InChannel))
+               if (feof(fp))
                        problem = "unexpected close";
-               else if (ferror(InChannel))
+               else if (ferror(fp))
                        problem = "I/O error";
                else
                        problem = "read timeout";
 # ifdef LOG
-               if (LogLevel > 0 && feof(InChannel))
+               if (LogLevel > 0 && feof(fp))
                        syslog(LOG_NOTICE,
-                           "collect: %s on connection from %s, sender=%s: %s\n",
-                           problem, host, e->e_from.q_paddr, errstring(errno));
+                           "collect: %s on connection from %.100s, sender=%s: %s",
+                           problem, host,
+                           shortenstring(e->e_from.q_paddr, 203),
+                           errstring(errno));
 # endif
-               if (feof(InChannel))
+               if (feof(fp))
                        usrerr("451 collect: %s on connection from %s, from=%s",
-                               problem, host, e->e_from.q_paddr);
+                               problem, host,
+                               shortenstring(e->e_from.q_paddr, 203));
                else
                        syserr("451 collect: %s on connection from %s, from=%s",
-                               problem, host, e->e_from.q_paddr);
+                               problem, host,
+                               shortenstring(e->e_from.q_paddr, 203));
 
                /* don't return an error indication */
                e->e_to = NULL;
@@ -364,84 +483,128 @@ readerr:
 
        eatheader(e, !requeueflag);
 
+       if (GrabTo && e->e_sendqueue == NULL)
+               usrerr("No recipient addresses found in header");
+
        /* collect statistics */
        if (OpMode != MD_VERIFY)
+       {
+               extern void markstats();
+
                markstats(e, (ADDRESS *) NULL);
+       }
 
        /*
        **  Add an Apparently-To: line if we have no recipient lines.
        */
 
-       if (hvalue("to", e) == NULL && hvalue("cc", e) == NULL &&
-           hvalue("bcc", e) == NULL && hvalue("apparently-to", e) == NULL)
+       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();
 
                /* create an Apparently-To: field */
                /*    that or reject the message.... */
-               for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+               switch (NoRecipientAction)
                {
-                       if (q->q_alias != NULL)
-                               continue;
-                       if (tTd(30, 3))
-                               printf("Adding Apparently-To: %s\n", q->q_paddr);
-                       addheader("Apparently-To", q->q_paddr, e);
+                 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_status = "5.2.3";
                usrerr("552 Message exceeds maximum fixed size (%ld)",
                        MaxMessageSize);
+# ifdef LOG
+               if (LogLevel > 6)
+                       syslog(LOG_NOTICE, "%s: message size (%ld) exceeds maximum (%ld)",
+                               e->e_id, e->e_msgsize, MaxMessageSize);
+# endif
        }
 
-       if ((e->e_dfp = fopen(e->e_df, "r")) == NULL)
+       /* check for illegal 8-bit data */
+       if (HasEightBits)
+       {
+               e->e_flags |= EF_HAS8BIT;
+               if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode))
+               {
+                       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", e->e_df);
+               syserr("Cannot reopen %s", dfname);
                finis();
        }
 }
-\f/*
-**  FLUSHEOL -- if not at EOL, throw away rest of input line.
-**
-**     Parameters:
-**             buf -- last line read in (checked for '\n'),
-**             fp -- file to be read from.
-**
-**     Returns:
-**             FALSE on error from sfgets(), TRUE otherwise.
-**
-**     Side Effects:
-**             none.
-*/
 
-bool
-flusheol(buf, fp)
-       char *buf;
-       FILE *fp;
-{
-       register char *p = buf;
-       char junkbuf[MAXLINE];
 
-       while (strchr(p, '\n') == NULL)
-       {
-               CollectErrorMessage = "553 header line too long";
-               CollectErrno = 0;
-               if (sfgets(junkbuf, MAXLINE, fp, TimeOuts.to_datablock,
-                               "long line flush") == NULL)
-                       return (FALSE);
-               p = junkbuf;
-       }
+static void
+collecttimeout(timeout)
+       time_t timeout;
+{
+       /* if no progress was made, die now */
+       if (!CollectProgress)
+               longjmp(CtxCollectTimeout, 1);
 
-       return (TRUE);
+       /* otherwise reset the timeout */
+       CollectTimeout = setevent(timeout, collecttimeout, timeout);
+       CollectProgress = FALSE;
 }
 \f/*
 **  TFERROR -- signal error on writing the temporary file.
 **
 **     Parameters:
 **             tf -- the file pointer for the temporary file.
+**             e -- the current envelope.
 **
 **     Returns:
 **             none.
@@ -451,21 +614,22 @@ flusheol(buf, fp)
 **             Arranges for following output to go elsewhere.
 */
 
+void
 tferror(tf, e)
        FILE *tf;
        register ENVELOPE *e;
 {
-       CollectErrno = errno;
+       setstat(EX_IOERR);
        if (errno == ENOSPC)
        {
                struct stat st;
                long avail;
                long bsize;
 
-               NoReturn = TRUE;
+               e->e_flags |= EF_NO_BODY_RETN;
                if (fstat(fileno(tf), &st) < 0)
                        st.st_size = 0;
-               (void) freopen(e->e_df, "w", tf);
+               (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))
@@ -476,7 +640,7 @@ tferror(tf, e)
                                st.st_size);
                fprintf(tf, "*** at %s due to lack of disk space for temp file.\n",
                        MyHostName);
-               avail = freespace(QueueDir, &bsize);
+               avail = freediskspace(QueueDir, &bsize);
                if (avail > 0)
                {
                        if (bsize > 1024)
@@ -486,12 +650,11 @@ tferror(tf, e)
                        fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n",
                                avail);
                }
-               CollectErrorMessage = "452 Out of disk space for temp file";
+               e->e_status = "4.3.1";
+               usrerr("452 Out of disk space for temp file");
        }
        else
-       {
-               CollectErrorMessage = "cannot write message body to disk (%s)";
-       }
+               syserr("collect: Cannot write tf%s", e->e_id);
        (void) freopen("/dev/null", "w", tf);
 }
 \f/*
@@ -525,6 +688,7 @@ char        *MonthList[] =
        NULL
 };
 
+void
 eatfrom(fm, e)
        char *fm;
        register ENVELOPE *e;
index 5559a74..1f426fd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)conf.c     8.89.1.3 (Berkeley) 3/7/95";
+static char sccsid[] = "@(#)conf.c     8.243 (Berkeley) 11/20/95";
 #endif /* not lint */
 
 # include "sendmail.h"
 # include "pathnames.h"
 # include <sys/ioctl.h>
 # include <sys/param.h>
-# include <netdb.h>
-# include <pwd.h>
 
 /*
 **  CONF.C -- Sendmail Configuration Tables.
@@ -80,44 +78,47 @@ static char sccsid[] = "@(#)conf.c  8.89.1.3 (Berkeley) 3/7/95";
 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,
-       "full-name",            H_ACHECK,
-       "return-receipt-to",    H_FROM|H_RECEIPTTO,
-       "errors-to",            H_FROM|H_ERRORSTO,
+       "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,
+       "full-name",                    H_ACHECK,
+       "return-receipt-to",            H_FROM|H_RECEIPTTO,
+       "errors-to",                    H_FROM|H_ERRORSTO,
 
                /* destination fields */
-       "to",                   H_RCPT,
-       "resent-to",            H_RCPT|H_RESENT,
-       "cc",                   H_RCPT,
-       "resent-cc",            H_RCPT|H_RESENT,
-       "bcc",                  H_RCPT|H_ACHECK,
-       "resent-bcc",           H_RCPT|H_ACHECK|H_RESENT,
-       "apparently-to",        H_RCPT,
+       "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,
+       "message-id",                   0,
+       "resent-message-id",            H_RESENT,
+       "message",                      H_EOH,
+       "text",                         H_EOH,
 
                /* date fields */
-       "date",                 0,
-       "resent-date",          H_RESENT,
+       "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,
+       "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,
-       "return-path",          H_FORCE|H_ACHECK,
+       "comments",                     H_FORCE,
+       "return-path",                  H_FORCE|H_ACHECK,
+       "content-transfer-encoding",    H_CTE,
+       "content-type",                 H_CTYPE,
+       "content-length",               H_ACHECK,
 
        NULL,                   0,
 };
@@ -159,25 +160,6 @@ struct prival PrivacyValues[] =
 */
 
 int    DtableSize =    50;             /* max open files; reset in 4.2bsd */
-
-
-/*
-**  Following should be config parameters (and probably will be in
-**  future releases).  In the meantime, setting these is considered
-**  unsupported, and is intentionally undocumented.
-*/
-
-#ifdef BROKENSMTPPEERS
-bool   BrokenSmtpPeers = TRUE;         /* set if you have broken SMTP peers */
-#else
-bool   BrokenSmtpPeers = FALSE;        /* set if you have broken SMTP peers */
-#endif
-#ifdef NOLOOPBACKCHECK
-bool   CheckLoopBack = FALSE;          /* set to check HELO loopback */
-#else
-bool   CheckLoopBack = TRUE;           /* set to check HELO loopback */
-#endif
-
 \f/*
 **  SETDEFAULTS -- set default values
 **
@@ -195,11 +177,24 @@ bool      CheckLoopBack = TRUE;           /* set to check HELO loopback */
 **             default values.
 */
 
-#define DAYS           * 24 * 60 * 60
+#define MINUTES                * 60
+#define HOURS          * 60 MINUTES
+#define DAYS           * 24 HOURS
+
+#ifndef MAXRULERECURSION
+# define MAXRULERECURSION      50      /* max ruleset recursion depth */
+#endif
 
+void
 setdefaults(e)
        register ENVELOPE *e;
 {
+       int i;
+       extern void inittimeouts();
+       extern void setdefuser();
+       extern void setupmaps();
+       extern void setupmailers();
+
        SpaceSub = ' ';                         /* option B */
        QueueLA = 8;                            /* option x */
        RefuseLA = 12;                          /* option X */
@@ -215,14 +210,29 @@ setdefaults(e)
        MaxHopCount = 25;                       /* option h */
        e->e_sendmode = SM_FORK;                /* option d */
        e->e_errormode = EM_PRINT;              /* option e */
-       SevenBit = FALSE;                       /* option 7 */
+       SevenBitInput = FALSE;                  /* option 7 */
        MaxMciCache = 1;                        /* option k */
-       MciCacheTimeout = 300;                  /* option K */
+       MciCacheTimeout = 5 MINUTES;            /* option K */
        LogLevel = 9;                           /* option L */
-       settimeouts(NULL);                      /* option r */
-       TimeOuts.to_q_return = 5 DAYS;          /* option T */
-       TimeOuts.to_q_warning = 0;              /* option T */
+       inittimeouts(NULL);                     /* option r */
        PrivacyFlags = 0;                       /* option p */
+#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";
+       HostsFile = _PATH_HOSTS;
+       MustQuoteChars = "@,;:\\()[].'";
+       MciInfoTimeout = 30 MINUTES;
+       MaxRuleRecursion = MAXRULERECURSION;
+       MaxAliasRecursion = 10;
+       ColonOkInAddr = TRUE;
        setdefuser();
        setupmaps();
        setupmailers();
@@ -233,13 +243,14 @@ setdefaults(e)
 **  SETDEFUSER -- set/reset DefUser using DefUid (for initgroups())
 */
 
+void
 setdefuser()
 {
        struct passwd *defpwent;
        static char defuserbuf[40];
 
        DefUser = defuserbuf;
-       if ((defpwent = getpwuid(DefUid)) != NULL)
+       if ((defpwent = sm_getpwuid(DefUid)) != NULL)
                strcpy(defuserbuf, defpwent->pw_name);
        else
                strcpy(defuserbuf, "nobody");
@@ -248,7 +259,7 @@ setdefuser()
 **  HOST_MAP_INIT -- initialize host class structures
 */
 
-bool host_map_init __P((MAP *map, char *args));
+bool   host_map_init __P((MAP *map, char *args));
 
 bool
 host_map_init(map, args)
@@ -282,14 +293,16 @@ host_map_init(map, args)
 **  SETUPMAILERS -- initialize default mailers
 */
 
+void
 setupmailers()
 {
        char buf[100];
+       extern void makemailer();
 
-       strcpy(buf, "prog, P=/bin/sh, F=lsD, A=sh -c $u");
+       strcpy(buf, "prog, P=/bin/sh, F=lsoD, T=DNS/RFC822/X-Unix, A=sh -c \201u");
        makemailer(buf);
 
-       strcpy(buf, "*file*, P=/dev/null, F=lsDFMPEu, A=FILE");
+       strcpy(buf, "*file*, P=[FILE], F=lsDFMPEou, T=DNS/RFC822/X-Unix, A=FILE");
        makemailer(buf);
 
        strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE");
@@ -317,6 +330,7 @@ setupmailers()
                s->s_mapclass.map_store = store; \
        }
 
+void
 setupmaps()
 {
        register STAB *s;
@@ -325,6 +339,7 @@ setupmaps()
        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);
@@ -338,40 +353,490 @@ setupmaps()
 
 #ifdef NIS
        MAPDEF("nis", NULL, MCF_ALIASOK,
-               map_parseargs, nis_map_open, nis_map_close,
-               nis_map_lookup, nis_map_store);
+               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 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, stab_map_close,
+               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);
 
-       /* host DNS lookup */
-       MAPDEF("host", NULL, 0,
-               host_map_init, null_map_open, null_map_close,
-               host_map_lookup, null_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);
 
-#if 0
-# ifdef USERDB
+#if USERDB
        /* user database */
-       MAPDEF("udb", ".db", 0,
-               udb_map_parse, null_map_open, null_map_close,
+       MAPDEF("userdb", ".db", 0,
+               map_parseargs, null_map_open, null_map_close,
                udb_map_lookup, null_map_store);
-# endif
 #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);
 }
 
 #undef MAPDEF
 \f/*
+**  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 /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
+}
+\f/*
+**  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];
+{
+       register FILE *fp;
+       int svcno;
+       static char buf[MAXLINE];
+
+#ifdef _USE_SUN_NSSWITCH_
+       struct __nsw_switchconfig *nsw_conf;
+       enum __nsw_parse_err pserr;
+       struct __nsw_lookup *lk;
+       int nsw_rc;
+       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.
+       */
+
+       for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
+               mapreturn[svcno] = 0;
+
+       svcno = 0;
+       fp = fopen(ServiceSwitchFile, "r");
+       if (fp != NULL)
+       {
+               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 (strcmp(buf, service) != 0)
+                               continue;
+
+                       /* got the right service -- extract data */
+                       do
+                       {
+                               while (isspace(*p))
+                                       p++;
+                               if (*p == '\0')
+                                       break;
+                               maptype[svcno++] = p;
+                               p = strpbrk(p, " \t");
+                               if (p != NULL)
+                                       *p++ = '\0';
+                       } while (p != NULL);
+                       fclose(fp);
+                       return svcno;
+               }
+
+               /* service was not found -- use compiled in default */
+               fclose(fp);
+       }
+#endif
+
+       /* if the service file doesn't work, use an absolute fallback */
+  punt:
+       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) && !defined(_USE_SUN_NSSWITCH_)
+               /* SunOS */
+               maptype[svcno++] = "nis";
+#  endif
+# endif
+               maptype[svcno++] = "files";
+               return svcno;
+       }
+       return -1;
+}
+\f/*
 **  USERNAME -- return the user id of the logged in user.
 **
 **     Parameters:
@@ -400,7 +865,7 @@ username()
                myname = getlogin();
                if (myname == NULL || myname[0] == '\0')
                {
-                       pw = getpwuid(RealUid);
+                       pw = sm_getpwuid(RealUid);
                        if (pw != NULL)
                                myname = newstr(pw->pw_name);
                }
@@ -409,10 +874,10 @@ username()
                        uid_t uid = RealUid;
 
                        myname = newstr(myname);
-                       if ((pw = getpwnam(myname)) == NULL ||
+                       if ((pw = sm_getpwnam(myname)) == NULL ||
                              (uid != 0 && uid != pw->pw_uid))
                        {
-                               pw = getpwuid(uid);
+                               pw = sm_getpwuid(uid);
                                if (pw != NULL)
                                        myname = newstr(pw->pw_name);
                        }
@@ -489,11 +954,14 @@ ttypath()
 **     forwarding or registration of users.
 **
 **     If the hosts are found to be incompatible, an error
-**     message should be given using "usrerr" and 0 should
-**     be returned.
+**     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.
 **
-**     'NoReturn' can be set to suppress the return-to-sender
-**     function; this should be done on huge messages.
+**     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.
@@ -505,6 +973,7 @@ ttypath()
 **             none (unless you include the usrerr stuff)
 */
 
+int
 checkcompat(to, e)
        register ADDRESS *to;
        register ENVELOPE *e;
@@ -523,11 +992,12 @@ checkcompat(to, e)
        register STAB *s;
 
        s = stab("arpa", ST_MAILER, ST_FIND);
-       if (s != NULL && e->e_from.q_mailer != LocalMailer &&
+       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");
-               /* NoReturn = TRUE; to supress return copy */
+               /* e->e_flags |= EF_NO_BODY_RETN; to supress body on return */
+               to->q_status = "5.7.1";
                return (EX_UNAVAILABLE);
        }
 # endif /* EXAMPLE_CODE */
@@ -544,19 +1014,47 @@ setsignal(sig, handler)
        int sig;
        sigfunc_t handler;
 {
-#if defined(SYS5SIGNALS) || defined(BSD4_3) || defined(_AUX_SOURCE)
+#if defined(SYS5SIGNALS) || defined(BSD4_3)
        return signal(sig, handler);
 #else
        struct sigaction n, o;
 
        bzero(&n, sizeof n);
        n.sa_handler = handler;
+# ifdef SA_RESTART
+       n.sa_flags = SA_RESTART;
+# endif
        if (sigaction(sig, &n, &o) < 0)
                return SIG_ERR;
        return o.sa_handler;
 #endif
 }
 \f/*
+**  RELEASESIGNAL -- release a held signal
+**
+**     Parameters:
+**             sig -- the signal to release.
+**
+**     Returns:
+**             0 on success.
+**             -1 on failure.
+*/
+
+int
+releasesignal(sig)
+       int sig;
+{
+#ifdef BSD4_3
+       return sigsetmask(sigblock(0) & ~(1 << sig));
+#else
+       sigset_t sset;
+
+       sigemptyset(&sset);
+       sigaddset(&sset, sig);
+       return sigprocmask(SIG_UNBLOCK, &sset, NULL);
+#endif
+}
+\f/*
 **  HOLDSIGS -- arrange to hold all signals
 **
 **     Parameters:
@@ -569,6 +1067,7 @@ setsignal(sig, handler)
 **             Arranges that signals are held.
 */
 
+void
 holdsigs()
 {
 }
@@ -587,6 +1086,7 @@ holdsigs()
 **             Arranges that signals are released.
 */
 
+void
 rlsesigs()
 {
 }
@@ -601,6 +1101,7 @@ rlsesigs()
 # include      <compat.h>
 #endif
 
+void
 init_md(argc, argv)
        int argc;
        char **argv;
@@ -608,6 +1109,32 @@ init_md(argc, argv)
 #ifdef _AUX_SOURCE
        setcompat(getcompat() | COMPAT_BSDPROT);
 #endif
+
+#ifdef VENDOR_DEFAULT
+       VendorCode = VENDOR_DEFAULT;
+#else
+       VendorCode = VENDOR_BERKELEY;
+#endif
+}
+\f/*
+**  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;
+{
 }
 \f/*
 **  GETLA -- get the current load average
@@ -632,15 +1159,36 @@ init_md(argc, argv)
 #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 */
 
 /* do guesses based on general OS type */
 #ifndef LA_TYPE
 # define LA_TYPE       LA_ZERO
 #endif
 
-#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT)
+#ifndef FSHIFT
+# if defined(unixpc)
+#  define FSHIFT       5
+# endif
 
-#include <nlist.h>
+# if defined(__alpha) || defined(IRIX)
+#  define FSHIFT       10
+# endif
+
+# if defined(_AIX3)
+#  define FSHIFT       16
+# endif
+#endif
+
+#ifndef FSHIFT
+# define FSHIFT                8
+#endif
+
+#ifndef FSCALE
+# define FSCALE                (1 << FSHIFT)
+#endif
 
 #ifndef LA_AVENRUN
 # ifdef SYSTEM5
@@ -650,39 +1198,38 @@ init_md(argc, argv)
 # endif
 #endif
 
-/* _PATH_UNIX should be defined in <paths.h> */
-#ifndef _PATH_UNIX
-# if defined(SYSTEM5)
-#  define _PATH_UNIX   "/unix"
-# else
-#  define _PATH_UNIX   "/vmunix"
-# endif
+/* _PATH_KMEM should be defined in <paths.h> */
+#ifndef _PATH_KMEM
+# define _PATH_KMEM    "/dev/kmem"
 #endif
 
-struct nlist Nl[] =
-{
-       { LA_AVENRUN },
-#define        X_AVENRUN       0
-       { 0 },
-};
-
-#ifndef FSHIFT
-# if defined(unixpc)
-#  define FSHIFT       5
-# endif
+#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT)
 
-# if defined(__alpha) || defined(IRIX)
-#  define FSHIFT       10
-# endif
+#include <nlist.h>
+
+#ifdef IRIX64
+# define nlist         nlist64
 #endif
 
-#ifndef FSHIFT
-# define FSHIFT                8
+/* _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
 
-#ifndef FSCALE
-# define FSCALE                (1 << FSHIFT)
+#ifdef _AUX_SOURCE
+struct nlist   Nl[2];
+#else
+struct nlist   Nl[] =
+{
+       { LA_AVENRUN },
+       { 0 },
+};
 #endif
+#define        X_AVENRUN       0
 
 getla()
 {
@@ -696,12 +1243,12 @@ getla()
        double avenrun[3];
 # endif
 #endif
-       extern off_t lseek();
        extern int errno;
+       extern off_t lseek();
 
        if (kmem < 0)
        {
-               kmem = open("/dev/kmem", 0, 0);
+               kmem = open(_PATH_KMEM, 0, 0);
                if (kmem < 0)
                {
                        if (tTd(3, 1))
@@ -710,7 +1257,17 @@ getla()
                        return (-1);
                }
                (void) fcntl(kmem, F_SETFD, 1);
+
+#ifdef _AUX_SOURCE
+               strcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN);
+               Nl[1].n_name[0] = '\0';
+#endif
+
+#ifdef _AIX3
+               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,
@@ -724,13 +1281,13 @@ getla()
                                        _PATH_UNIX, LA_AVENRUN);
                        return (-1);
                }
-#ifdef IRIX
-               Nl[X_AVENRUN].n_value &= 0x7fffffff;
+#ifdef NAMELISTMASK
+               Nl[X_AVENRUN].n_value &= NAMELISTMASK;
 #endif
        }
        if (tTd(3, 20))
                printf("getla: symbol address = %#x\n", Nl[X_AVENRUN].n_value);
-       if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, 0) == -1 ||
+       if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 ||
            read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
        {
                /* thank you Ian */
@@ -738,7 +1295,7 @@ getla()
                        printf("getla: lseek or read: %s\n", errstring(errno));
                return (-1);
        }
-#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT)
+# if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT)
        if (tTd(3, 5))
        {
                printf("getla: avenrun = %d", avenrun[0]);
@@ -749,7 +1306,7 @@ getla()
        if (tTd(3, 1))
                printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
        return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
-#else
+# else /* LA_TYPE == LA_FLOAT */
        if (tTd(3, 5))
        {
                printf("getla: avenrun = %g", avenrun[0]);
@@ -760,28 +1317,113 @@ getla()
        if (tTd(3, 1))
                printf("getla: %d\n", (int) (avenrun[0] +0.5));
        return ((int) (avenrun[0] + 0.5));
-#endif
+# endif
 }
 
-#else
-#if LA_TYPE == LA_SUBR
+#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 */
 
-#ifdef DGUX
+#if LA_TYPE == LA_DGUX
 
-#include <sys/dg_sys_info.h>
+# include <sys/dg_sys_info.h>
 
-int getla()
+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));
 }
 
-#else
+#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];
@@ -797,8 +1439,8 @@ getla()
        return ((int) (avenrun[0] + 0.5));
 }
 
-#endif /* DGUX */
-#else
+#endif /* LA_TYPE == LA_SUBR */
+
 #if LA_TYPE == LA_MACH
 
 /*
@@ -832,8 +1474,8 @@ getla()
        return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE;
 }
 
+#endif /* LA_TYPE == LA_MACH */
 
-#else
 #if LA_TYPE == LA_PROCSTR
 
 /*
@@ -856,7 +1498,7 @@ getla()
        FILE *fp;
 
        fp = fopen(_PATH_LOADAVG, "r");
-       if (fp == NULL) 
+       if (fp == NULL)
        {
                if (tTd(3, 1))
                        printf("getla: fopen(%s): %s\n",
@@ -879,7 +1521,9 @@ getla()
        return ((int) (avenrun + 0.5));
 }
 
-#else
+#endif /* LA_TYPE == LA_PROCSTR */
+
+#if LA_TYPE == LA_ZERO
 
 getla()
 {
@@ -888,10 +1532,7 @@ getla()
        return (0);
 }
 
-#endif
-#endif
-#endif
-#endif
+#endif /* LA_TYPE == LA_ZERO */
 
 
 /*
@@ -962,11 +1603,28 @@ shouldqueue(pri, ctime)
        long pri;
        time_t ctime;
 {
+       bool rval;
+
+       if (tTd(3, 30))
+               printf("shouldqueue: CurrentLA=%d, pri=%d: ", CurrentLA, pri);
        if (CurrentLA < QueueLA)
+       {
+               if (tTd(3, 30))
+                       printf("FALSE (CurrentLA < QueueLA)\n");
                return (FALSE);
-       if (CurrentLA >= RefuseLA)
+       }
+#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);
-       return (pri > (QueueFactor / (CurrentLA - QueueLA + 1)));
+       }
+#endif
+       rval = pri > (QueueFactor / (CurrentLA - QueueLA + 1));
+       if (tTd(3, 30))
+               printf("%s (by calculation)\n", rval ? "TRUE" : "FALSE");
+       return rval;
 }
 \f/*
 **  REFUSECONNECTIONS -- decide if connections should be refused
@@ -980,19 +1638,38 @@ shouldqueue(pri, ctime)
 **             FALSE if we should accept new work.
 **
 **     Side Effects:
-**             none.
+**             Sets process title when it is rejecting connections.
 */
 
 bool
 refuseconnections()
 {
+       extern bool enoughdiskspace();
+       extern void setproctitle __P((const char *, ...));
+
 #ifdef XLA
        if (!xla_smtp_ok())
                return TRUE;
 #endif
 
-       /* this is probably too simplistic */
-       return (CurrentLA >= RefuseLA);
+       if (CurrentLA >= RefuseLA)
+       {
+               setproctitle("rejecting connections: load average: %d",
+                       CurrentLA);
+       }
+       else if (!enoughdiskspace(MinBlocksFree + 1))
+       {
+               setproctitle("rejecting connections: min free: %d",
+                       MinBlocksFree);
+       }
+       else if (MaxChildren > 0 && CurChildren >= MaxChildren)
+       {
+               setproctitle("rejecting connections: maximum children: %d",
+                       CurChildren);
+       }
+       else
+               return FALSE;
+       return TRUE;
 }
 \f/*
 **  SETPROCTITLE -- set process title for ps
@@ -1009,54 +1686,135 @@ refuseconnections()
 **             display the title.
 */
 
-#ifdef SETPROCTITLE
-# ifdef HASSETPROCTITLE
-   *** ERROR ***  Cannot have both SETPROCTITLE and HASSETPROCTITLE defined
-# endif
-# ifdef __hpux
+#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 */
+
+#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
-# ifdef BSD4_4
+# if SPT_TYPE == SPT_PSSTRINGS
 #  include <machine/vmparam.h>
 #  include <sys/exec.h>
-#  ifdef __bsdi__
-#   undef PS_STRINGS   /* BSDI 1.0 doesn't do PS_STRINGS as we expect */
-#   define PROCTITLEPAD        '\0'
-#  endif
-#  ifdef PS_STRINGS
-#   define SETPROC_STATIC static
+#  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
-# ifndef SETPROC_STATIC
+
+# if SPT_TYPE == SPT_PSSTRINGS
+#  define SETPROC_STATIC       static
+# else
 #  define SETPROC_STATIC
 # endif
-#endif
 
-#ifndef PROCTITLEPAD
-# define PROCTITLEPAD  ' '
-#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;
+       extern char **environ;
+
+       /*
+        **  Move the environment so setproctitle can use the space at
+       **  the top of memory.
+       */
+
+       for (i = 0; envp[i] != NULL; i++)
+               continue;
+       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;
+       if (i > 0)
+               LastArgv = envp[i - 1] + strlen(envp[i - 1]);
+       else
+               LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
+}
+
+#if SPT_TYPE != SPT_BUILTIN
 
-#ifndef HASSETPROCTITLE
 
 /*VARARGS1*/
-#ifdef __STDC__
-setproctitle(char *fmt, ...)
-#else
+void
+# ifdef __STDC__
+setproctitle(const char *fmt, ...)
+# else
 setproctitle(fmt, va_alist)
-       char *fmt;
+       const char *fmt;
        va_dcl
-#endif
+# endif
 {
-# ifdef SETPROCTITLE
+# if SPT_TYPE != SPT_NONE
        register char *p;
        register int i;
-       SETPROC_STATIC char buf[MAXLINE];
+       SETPROC_STATIC char buf[SPT_BUFSIZE];
        VA_LOCAL_DECL
-#  ifdef __hpux
+#  if SPT_TYPE == SPT_PSTAT
        union pstun pst;
 #  endif
-       extern char **Argv;
-       extern char *LastArgv;
+#  if SPT_TYPE == SPT_SCO
+       off_t seek_off;
+       static int kmem = -1;
+       static int kmempid = -1;
+       struct user u;
+#  endif
 
        p = buf;
 
@@ -1066,19 +1824,39 @@ setproctitle(fmt, va_alist)
 
        /* print the argument string */
        VA_START(fmt);
-       (void) vsprintf(p, fmt, ap);
+       (void) vsnprintf(p, sizeof buf - (p - buf), fmt, ap);
        VA_END;
 
        i = strlen(buf);
 
-#  ifdef __hpux
+#  if SPT_TYPE == SPT_PSTAT
        pst.pst_command = buf;
        pstat(PSTAT_SETCMD, pst, i, 0, 0);
-#  else
-#   ifdef PS_STRINGS
+#  endif
+#  if SPT_TYPE == SPT_PSSTRINGS
        PS_STRINGS->ps_nargvstr = 1;
        PS_STRINGS->ps_argvstr = buf;
-#   else
+#  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, (char *) 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;
@@ -1087,18 +1865,18 @@ setproctitle(fmt, va_alist)
        (void) strcpy(Argv[0], buf);
        p = &Argv[0][i];
        while (p < LastArgv)
-               *p++ = PROCTITLEPAD;
-#   endif
+               *p++ = SPT_PADCHAR;
+       Argv[1] = NULL;
 #  endif
-# endif /* SETPROCTITLE */
+# endif /* SPT_TYPE != SPT_NONE */
 }
 
-#endif
+#endif /* SPT_TYPE != SPT_BUILTIN */
 \f/*
 **  REAPCHILD -- pick up the body of my child, lest it become a zombie
 **
 **     Parameters:
-**             none.
+**             sig -- the signal that got us here (unused).
 **
 **     Returns:
 **             none.
@@ -1108,7 +1886,8 @@ setproctitle(fmt, va_alist)
 */
 
 void
-reapchild()
+reapchild(sig)
+       int sig;
 {
        int olderrno = errno;
 # ifdef HASWAITPID
@@ -1127,18 +1906,19 @@ reapchild()
 #endif
                        break;
                }
+               CurChildren--;
        }
 # else
 # ifdef WNOHANG
        union wait status;
 
        while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0)
-               continue;
+               CurChildren--;
 # else /* WNOHANG */
        auto int status;
 
        while (wait(&status) > 0)
-               continue;
+               CurChildren--;
 # endif /* WNOHANG */
 # endif
 # ifdef SYS5SIGNALS
@@ -1147,6 +1927,107 @@ reapchild()
        errno = olderrno;
 }
 \f/*
+**  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 = index(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
+\f/*
 **  UNSETENV -- remove a variable from the environment
 **
 **     Not needed on newer systems.
@@ -1284,7 +2165,7 @@ uname(name)
                        return (0);
        }
 #endif
-       
+
        return (-1);
 }
 #endif /* HASUNAME */
@@ -1353,10 +2234,11 @@ fsync(fd)
 **  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.
+**     instead of a long.  This patches things.  Only needed on versions
+**     prior to 5.4.3.
 */
 
-#ifdef DGUX
+#ifdef DGUX_5_4_2
 
 #undef inet_addr
 
@@ -1399,13 +2281,14 @@ static char sccsid[] = "@(#)getopt.c    4.3 (Berkeley) 3/9/86";
  * get option letter from argument vector
  */
 #ifdef _CONVEX_SOURCE
-extern int     optind, opterr;
+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 */
-#endif
 int    optopt = 0;             /* character checked for validity */
 char   *optarg = NULL;         /* argument associated with option */
+#endif
 
 #define BADCH  (int)'?'
 #define EMSG   ""
@@ -1467,12 +2350,12 @@ getopt(nargc,nargv,ostr)
 #define MAXARG 16
 
 vfprintf(fp, fmt, ap)
-       FILE *  fp;
-       char *  fmt;
-       char ** ap;
+       FILE *fp;
+       char *fmt;
+       char **ap;
 {
-       char *  bp[MAXARG];
-       int     i = 0;
+       char *bp[MAXARG];
+       int i = 0;
 
        while (*ap && i < MAXARG)
                bp[i++] = *ap++;
@@ -1482,27 +2365,78 @@ vfprintf(fp, fmt, ap)
                         bp[12], bp[13], bp[14], bp[15]);
 }
 
-vsprintf(s, fmt, ap)
-       char *  s;
-       char *  fmt;
-       char ** ap;
-{
-       char *  bp[MAXARG];
-       int     i = 0;
+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
+\f/*
+**  SNPRINTF, VSNPRINT -- counted versions of printf
+**
+**     These are at best crude emulations.
+*/
+
+#if !HASSNPRINTF
+
+void
+# ifdef __STDC__
+snprintf(char *buf, size_t bufsize, const char *fmt, ...)
+# else
+snprintf(buf, bufsize, fmt, va_alist)
+       char *buf;
+       size_t bufsize;
+       const char *fmt;
+       va_dcl
+# endif
+{
+       VA_LOCAL_DECL
+
+       VA_START(fmt);
+       vsprintf(buf, fmt, ap);
+       VA_END;
+# if defined(XDEBUG) && defined(LOG)
+       if (strlen(buf) > bufsize)
+               syslog(LOG_ALERT, "INTERNAL ERROR: snprintf overflow: %s",
+                       shortenstring(buf, 200));
+# endif
+}
+
 
-       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]);
+#ifndef luna2
+void
+vsnprintf(buf, bufsize, fmt, ap)
+       char *buf;
+       size_t bufsize;
+       const char *fmt;
+       va_list ap;
+{
+       vsprintf(buf, fmt, ap);
+# if defined(XDEBUG) && defined(LOG)
+       if (strlen(buf) > bufsize)
+               syslog(LOG_ALERT, "INTERNAL ERROR: vsnprintf overflow: %s",
+                       shortenstring(buf, 200));
+# endif
 }
+#endif
 
 #endif
 \f/*
 **  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:
@@ -1516,19 +2450,45 @@ vsprintf(s, fmt, ap)
 #  define _PATH_SHELLS "/etc/shells"
 # endif
 
+# ifdef _AIX3
+#  include <userconf.h>
+#  include <usersec.h>
+# endif
+
 char   *DefaultUserShells[] =
 {
-       "/bin/sh",
+       "/bin/sh",              /* standard shell */
        "/usr/bin/sh",
-       "/bin/csh",
+       "/bin/csh",             /* C shell */
        "/usr/bin/csh",
 #ifdef __hpux
-       "/bin/rsh",
-       "/bin/ksh",
-       "/bin/rksh",
+# 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",
+       "/usr/bin/keysh",       /* key shell (extended Korn shell) */
        "/bin/posix/sh",
+# endif
+#endif
+#ifdef _AIX3
+       "/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
        NULL
 };
@@ -1538,14 +2498,16 @@ char    *DefaultUserShells[] =
 #define WILDCARD_SHELL "/SENDMAIL/ANY/SHELL/"
 
 bool
-usershellok(shell)
+usershellok(user, shell)
+       char *user;
        char *shell;
 {
 #if HASGETUSERSHELL
        register char *p;
        extern char *getusershell();
 
-       if (shell == NULL || shell[0] == '\0')
+       if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
+           ConfigLevel <= 1)
                return TRUE;
 
        setusershell();
@@ -1555,12 +2517,41 @@ usershellok(shell)
        endusershell();
        return p != NULL;
 #else
+# if USEGETCONFATTR
+       auto char *v;
+# endif
        register FILE *shellf;
        char buf[MAXLINE];
 
-       if (shell == NULL || shell[0] == '\0')
+       if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't'))
                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)
        {
@@ -1599,7 +2590,7 @@ usershellok(shell)
 #endif
 }
 \f/*
-**  FREESPACE -- see how much free space is on the queue filesystem
+**  FREEDISKSPACE -- see how much free space is on the queue filesystem
 **
 **     Only implemented if you have statfs.
 **
@@ -1646,7 +2637,7 @@ usershellok(shell)
 #endif
 
 long
-freespace(dir, bsize)
+freediskspace(dir, bsize)
        char *dir;
        long *bsize;
 {
@@ -1655,26 +2646,25 @@ freespace(dir, bsize)
        struct ustat fs;
        struct stat statbuf;
 #  define FSBLOCKSIZE  DEV_BSIZE
-#  define f_bavail     f_tfree
+#  define SFS_BAVAIL   f_tfree
 # else
 #  if defined(ultrix)
        struct fs_data fs;
-#   define f_bavail    fd_bfreen
-#   define FSBLOCKSIZE fs.fd_bsize
+#   define SFS_BAVAIL  fd_bfreen
+#   define FSBLOCKSIZE 1024L
 #  else
 #   if SFS_TYPE == SFS_STATVFS
        struct statvfs fs;
-#    define FSBLOCKSIZE        fs.f_bsize
+#    define FSBLOCKSIZE        fs.f_frsize
 #   else
        struct statfs fs;
 #    define FSBLOCKSIZE        fs.f_bsize
-#    if defined(_SCO_unix_) || defined(IRIX) || defined(apollo)
-#     define f_bavail f_bfree
-#    endif
 #   endif
 #  endif
 # endif
-       extern int errno;
+# 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)
@@ -1682,23 +2672,30 @@ freespace(dir, bsize)
 #  if SFS_TYPE == SFS_4ARGS
        if (statfs(dir, &fs, sizeof fs, 0) == 0)
 #  else
-#   if defined(ultrix)
-       if (statfs(dir, &fs) > 0)
+#   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;
-               return (fs.f_bavail);
+               if (fs.SFS_BAVAIL < 0)
+                       return 0;
+               else
+                       return fs.SFS_BAVAIL;
        }
 #endif
        return (-1);
 }
 \f/*
-**  ENOUGHSPACE -- check to see if there is enough free space on the queue fs
+**  ENOUGHDISKSPACE -- is there enough free space on the queue fs?
 **
 **     Only implemented if you have statfs.
 **
@@ -1713,7 +2710,7 @@ freespace(dir, bsize)
 */
 
 bool
-enoughspace(msize)
+enoughdiskspace(msize)
        long msize;
 {
        long bfree, bsize;
@@ -1721,14 +2718,14 @@ enoughspace(msize)
        if (MinBlocksFree <= 0 && msize <= 0)
        {
                if (tTd(4, 80))
-                       printf("enoughspace: no threshold\n");
+                       printf("enoughdiskspace: no threshold\n");
                return TRUE;
        }
 
-       if ((bfree = freespace(QueueDir, &bsize)) >= 0)
+       if ((bfree = freediskspace(QueueDir, &bsize)) >= 0)
        {
                if (tTd(4, 80))
-                       printf("enoughspace: bavail=%ld, need=%ld\n",
+                       printf("enoughdiskspace: bavail=%ld, need=%ld\n",
                                bfree, msize);
 
                /* convert msize to block count */
@@ -1742,14 +2739,16 @@ enoughspace(msize)
                        if (LogLevel > 0)
                                syslog(LOG_ALERT,
                                        "%s: low on space (have %ld, %s needs %ld in %s)",
-                                       CurEnv->e_id, bfree,
-                                       CurHostName, msize, QueueDir);
+                                       CurEnv->e_id == NULL ? "[NOQUEUE]" : CurEnv->e_id,
+                                       bfree,
+                                       CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
+                                       msize, QueueDir);
 #endif
                        return FALSE;
                }
        }
        else if (tTd(4, 80))
-               printf("enoughspace failure: min=%ld, need=%ld: %s\n",
+               printf("enoughdiskspace failure: min=%ld, need=%ld: %s\n",
                        MinBlocksFree, msize, errstring(errno));
        return TRUE;
 }
@@ -1850,6 +2849,7 @@ transienterror(err)
 #if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR))
          case ENOSR:                   /* Out of streams resources */
 #endif
+         case EOPENTIMEOUT:            /* PSEUDO: open timed out */
                return TRUE;
        }
 
@@ -1885,7 +2885,7 @@ lockfile(fd, filename, ext, type)
 
        if (ext == NULL)
                ext = "";
-               
+
        bzero(&lfd, sizeof lfd);
        if (bitset(LOCK_UN, type))
                lfd.l_type = F_UNLCK;
@@ -2011,7 +3011,7 @@ chownsafe(fd)
        rval = fchown(tfd, DefUid, DefGid) != 0;
        close(tfd);
        unlink(s);
-       setreuid(o_uid, o_euid);
+       setresuid(o_uid, o_euid, -1);
        setresgid(o_gid, o_egid, -1);
        return rval;
 #else
@@ -2023,18 +3023,62 @@ chownsafe(fd)
 #  endif
 # else
 #  ifdef _PC_CHOWN_RESTRICTED
-       return fpathconf(fd, _PC_CHOWN_RESTRICTED) > 0;
-#  else
-#   ifdef BSD
+       int rval;
+
+       /*
+       **  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 (errno == 0)
+               return rval > 0;
+#  endif
+#  ifdef BSD
        return TRUE;
-#   else
+#  else
        return FALSE;
-#   endif
 #  endif
 # endif
 #endif
 }
 \f/*
+**  RESETLIMITS -- reset system controlled resource limits
+**
+**     This is to avoid denial-of-service attacks
+**
+**     Parameters:
+**             none
+**
+**     Returns:
+**             none
+*/
+
+#if HASSETRLIMIT
+# ifdef apollo
+#  include <sys/time.h>
+# endif
+# include <sys/resource.h>
+#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);
+#else
+# if HASULIMIT
+       (void) ulimit(2, 0x3fffff);
+# endif
+#endif
+}
+\f/*
 **  GETCFNAME -- return the name of the .cf file.
 **
 **     Some systems (e.g., NeXT) determine this dynamically.
@@ -2043,18 +3087,21 @@ chownsafe(fd)
 char *
 getcfname()
 {
+
        if (ConfFile != NULL)
                return ConfFile;
-#ifdef NETINFO
+#if NETINFO
        {
                extern char *ni_propval();
                char *cflocation;
 
-               cflocation = ni_propval("/locations/sendmail", "sendmail.cf");
+               cflocation = ni_propval("/locations", NULL, "sendmail",
+                                       "sendmail.cf", '\0');
                if (cflocation != NULL)
                        return cflocation;
        }
 #endif
+
        return _PATH_SENDMAILCF;
 }
 \f/*
@@ -2079,13 +3126,49 @@ 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;
 }
 \f/*
+**  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.
+*/
+
+void
+vendor_pre_defaults(e)
+       ENVELOPE *e;
+{
+}
+
+
+void
+vendor_post_defaults(e)
+       ENVELOPE *e;
+{
+}
+\f/*
 **  STRTOL -- convert string to long integer
 **
 **     For systems that don't have it in the C library.
@@ -2191,40 +3274,127 @@ strtol(nptr, endptr, base)
 
 #endif
 \f/*
-**  SOLARIS_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
+**  STRSTR -- find first substring in string
+**
+**     Parameters:
+**             big -- the big (full) string.
+**             little -- the little (sub) string.
 **
-**     Solaris versions prior through 2.3 don't properly deliver a
-**     canonical h_name field.  This tries to work around it.
+**     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 SOLARIS
+#ifdef NEEDSTRSTR
+
+char *
+strstr(big, little)
+       char *big;
+       char *little;
+{
+       register char *p = big;
+       int l;
 
-extern int     h_errno;
+       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
+\f/*
+**  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 *
-solaris_gethostbyname(name)
-       const char *name;
+sm_gethostbyname(name)
+       char *name;
 {
-# ifdef SOLARIS_2_3
+       struct hostent *h;
+#if defined(SOLARIS) && SOLARIS < 204 || defined(sony_news) && defined(__svr4)
+# if SOLARIS == 203
        static struct hostent hp;
        static char buf[1000];
        extern struct hostent *_switch_gethostbyname_r();
 
-       return _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
+       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();
 
-       return __switch_gethostbyname(name);
+       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) > 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 *
-solaris_gethostbyaddr(addr, len, type)
-       const char *addr;
+sm_gethostbyaddr(addr, len, type)
+       char *addr;
        int len;
        int type;
 {
-# ifdef SOLARIS_2_3
+#if defined(SOLARIS) && SOLARIS < 204
+# if SOLARIS == 203
        static struct hostent hp;
        static char buf[1000];
        extern struct hostent *_switch_gethostbyaddr_r();
@@ -2235,122 +3405,155 @@ solaris_gethostbyaddr(addr, len, type)
 
        return __switch_gethostbyaddr(addr, len, type);
 # endif
+#else
+       return gethostbyaddr(addr, len, type);
+#endif
 }
+\f/*
+**  SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid
+*/
 
-#endif
+struct passwd *
+sm_getpwnam(user)
+       char *user;
+{
+       return getpwnam(user);
+}
+
+struct passwd *
+sm_getpwuid(uid)
+       UID_T uid;
+{
+       return getpwuid(uid);
+}
 \f/*
-**  NI_PROPVAL -- netinfo property value lookup routine
+**  LOAD_IF_NAMES -- load interface-specific names into $=w
 **
 **     Parameters:
-**             directory -- the Netinfo directory name.
-**             propname -- the Netinfo property name.
+**             none.
 **
 **     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 location of the config file.
+**             none.
 **
-**     Notes:
-**             Caller should free the return value of ni_proval
+**     Side Effects:
+**             Loads $=w with the names of all the interfaces.
 */
 
-#ifdef NETINFO
-
-# include <netinfo/ni.h>
-
-# define LOCAL_NETINFO_DOMAIN    "."
-# define PARENT_NETINFO_DOMAIN   ".."
-# define MAX_NI_LEVELS           256
+#ifdef SIOCGIFCONF
+struct rtentry;
+struct mbuf;
+# include <arpa/inet.h>
+# ifndef SUNOS403
+#  include <sys/time.h>
+# endif
+# include <net/if.h>
+#endif
 
-char *
-ni_propval(directory, propname)
-       char *directory;
-       char *propname;
+void
+load_if_names()
 {
-       char *propval = NULL;
+#ifdef SIOCGIFCONF
+       int s;
        int i;
-       void *ni = NULL;
-       void *lastni = NULL;
-       ni_status nis;
-       ni_id nid;
-       ni_namelist ninl;
+        struct ifconf ifc;
+       char interfacebuf[1024];
 
-       /*
-       **  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.
-       */
+       s = socket(AF_INET, SOCK_DGRAM, 0);
+       if (s == -1)
+               return;
 
-       for (i = 0; i < MAX_NI_LEVELS; ++i)
+       /* get the list of known IP address from the kernel */
+        ifc.ifc_buf = interfacebuf;
+        ifc.ifc_len = sizeof interfacebuf;
+       if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
        {
-               if (i == 0)
-               {
-                       nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
-               }
-               else
-               {
-                       if (lastni != NULL)
-                               ni_free(lastni);
-                       lastni = ni;
-                       nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
-               }
+               if (tTd(0, 4))
+                       printf("SIOGIFCONF failed: %s\n", errstring(errno));
+               close(s);
+               return;
+       }
+       close(s);
 
-               /*
-               **  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!
-               */
+       /* scan the list of IP address */
+       if (tTd(0, 40))
+               printf("scanning for interface specific names, ifc_len=%d\n",
+                       ifc.ifc_len);
 
-               if (nis != 0)
-                       break;
+       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;
+               char ip_addr[256];
+               extern char *inet_ntoa();
+               extern struct hostent *gethostbyaddr();
+
+#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;
 
-               /*
-               **  Find the path to the server information.
-               */
+               if (tTd(0, 20))
+                       printf("%s\n", anynet_ntoa((SOCKADDR *) sa));
 
-               if (ni_pathsearch(ni, &nid, directory) != 0)
+               /* for some reason gcc 2.3 pukes on || here */
+               if (!bitset(IFF_UP, ifr->ifr_flags))
                        continue;
-
-               /*
-               **  Find "host" information.
-               */
-
-               if (ni_lookupprop(ni, &nid, propname, &ninl) != 0)
+               if (ifr->ifr_addr.sa_family != AF_INET)
                        continue;
 
-               /*
-               **  If there's only one name in
-               **  the list, assume we've got
-               **  what we want.
-               */
+               /* extract IP address from the list*/
+               ia = (((struct sockaddr_in *) sa)->sin_addr);
 
-               if (ninl.ni_namelist_len == 1)
+               /* save IP address in text from */
+               (void) sprintf(ip_addr, "[%.*s]",
+                       sizeof ip_addr - 3,
+                       inet_ntoa(((struct sockaddr_in *) sa)->sin_addr));
+               if (!wordinclass(ip_addr, 'w'))
                {
-                       propval = ni_name_dup(ninl.ni_namelist_val[0]);
-                       break;
+                       setclass('w', ip_addr);
+                       if (tTd(0, 4))
+                               printf("\ta.k.a.: %s\n", ip_addr);
                }
-       }
 
-       /*
-       **  Clean up.
-       */
+               /* skip "loopback" interface "lo" */
+               if (strcmp("lo0", ifr->ifr_name) == 0)
+                       continue;
+
+               /* lookup name with IP address */
+               hp = sm_gethostbyaddr((char *) &ia, sizeof(ia), AF_INET);
+               if (hp == NULL)
+               {
+                       syslog(LOG_CRIT, "gethostbyaddr() failed for %.100s\n",
+                               inet_ntoa(ia));
+                       continue;
+               }
 
-       if (ni != NULL)
-               ni_free(ni);
-       if (lastni != NULL && ni != lastni)
-               ni_free(lastni);
+               /* 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);
+               }
 
-       return propval;
+               /* 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++;
+               }
+       }
+#endif
 }
-
-#endif /* NETINFO */
 \f/*
 **  HARD_SYSLOG -- call syslog repeatedly until it works
 **
@@ -2364,6 +3567,7 @@ ni_propval(directory, propname)
 # undef syslog
 
 # ifdef __STDC__
+void
 hard_syslog(int pri, char *msg, ...)
 # else
 hard_syslog(pri, msg, va_alist)
@@ -2377,7 +3581,7 @@ hard_syslog(pri, msg, va_alist)
        VA_LOCAL_DECL;
 
        VA_START(msg);
-       vsprintf(buf, msg, ap);
+       vsnprintf(buf, sizeof buf, msg, ap);
        VA_END;
 
        for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, "%s", buf) < 0; )
@@ -2385,3 +3589,167 @@ hard_syslog(pri, msg, va_alist)
 }
 
 #endif
+\f/*
+**  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
+\f/*
+**  Compile-Time options
+*/
+
+char   *CompileOptions[] =
+{
+#if HESIOD
+       "HESIOD",
+#endif
+#if HES_GETMAILHOST
+       "HES_GETMAILHOST",
+#endif
+#ifdef LOG
+       "LOG",
+#endif
+#if MATCHGECOS
+       "MATCHGECOS",
+#endif
+#if MIME8TO7
+       "MIME8TO7",
+#endif
+#if NAMED_BIND
+       "NAMED_BIND",
+#endif
+#if 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
+#if NEWDB
+       "NEWDB",
+#endif
+#if NIS
+       "NIS",
+#endif
+#if NISPLUS
+       "NISPLUS",
+#endif
+#if SCANF
+       "SCANF",
+#endif
+#if SUID_ROOT_FILES_OK
+       "SUID_ROOT_FILES_OK",
+#endif
+#if USERDB
+       "USERDB",
+#endif
+#if XDEBUG
+       "XDEBUG",
+#endif
+#if XLA
+       "XLA",
+#endif
+       NULL
+};
+
+
+/*
+**  OS compile options.
+*/
+
+char   *OsCompileOptions[] =
+{
+#if HASFCHMOD
+       "HASFCHMOD",
+#endif
+#if HASFLOCK
+       "HASFLOCK",
+#endif
+#if HASGETUSERSHELL
+       "HASGETUSERSHELL",
+#endif
+#if HASINITGROUPS
+       "HASINITGROUPS",
+#endif
+#if HASLSTAT
+       "HASLSTAT",
+#endif
+#if HASSETREUID
+       "HASSETREUID",
+#endif
+#if HASSETSID
+       "HASSETSID",
+#endif
+#if HASSETVBUF
+       "HASSETVBUF",
+#endif
+#if HASSNPRINTF
+       "HASSNPRINTF",
+#endif
+#if HASUNAME
+       "HASUNAME",
+#endif
+#if IDENTPROTO
+       "IDENTPROTO",
+#endif
+#if IP_SRCROUTE
+       "IP_SRCROUTE",
+#endif
+#if NEEDFSYNC
+       "NEEDFSYNC",
+#endif
+#if NOFTRUNCATE
+       "NOFTRUNCATE",
+#endif
+#if SYS5SETPGRP
+       "SYS5SETPGRP",
+#endif
+#if SYSTEM5
+       "SYSTEM5",
+#endif
+#if USESETEUID
+       "USESETEUID",
+#endif
+       NULL
+};
index 62344c3..9ac4d8f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     @(#)conf.h      8.104 (Berkeley) 4/17/94
+ *     @(#)conf.h      8.220 (Berkeley) 11/29/95
  */
 
 /*
 **  CONF.H -- All user-configurable parameters for sendmail
+**
+**     Send updates to sendmail@Sendmail.ORG so they will be
+**     included in the next release.
 */
 
+struct rusage; /* forward declaration to get gcc to shut up in wait.h */
+
 # include <sys/param.h>
 # include <sys/types.h>
 # include <sys/stat.h>
 # include <sys/file.h>
 # include <sys/wait.h>
+# include <limits.h>
 # include <fcntl.h>
 # include <signal.h>
+# include <netdb.h>
+# include <pwd.h>
 
 /**********************************************************************
 **  Table sizes, etc....
@@ -56,7 +64,7 @@
 # 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     100             /* max # of sets of rewriting rules */
+# define MAXRWSETS     200             /* max # of sets of rewriting rules */
 # define MAXPRIORITIES 25              /* max values for Precedence: field */
 # define MAXMXHOSTS    20              /* max # of MX records */
 # define SMTPLINELIM   990             /* maximum SMTP line length */
 # define MEMCHUNKSIZE  1024            /* chunk size for memory allocation */
 # define MAXUSERENVIRON        100             /* max envars saved, must be >= 3 */
 # define MAXALIASDB    12              /* max # of alias databases */
-
-# ifndef QUEUESIZE
-# define QUEUESIZE     1000            /* max # of jobs per queue run */
-# endif
+# 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 */
 
 /**********************************************************************
 **  Compilation options.
-**
-**     #define these if they are available; comment them out otherwise.
+**     #define these to 1 if they are available;
+**     #define them to 0 otherwise.
+**  All can be overridden from Makefile.
 **********************************************************************/
 
-# define LOG           1       /* enable logging */
-# define UGLYUUCP      1       /* output ugly UUCP From lines */
-# define NETUNIX       1       /* include unix domain support */
-# define NETINET       1       /* include internet support */
-# define SETPROCTITLE  1       /* munge argv to display current status */
-# define MATCHGECOS    1       /* match user names from gecos field */
-# define XDEBUG                1       /* enable extended debugging */
-# ifdef NEWDB
-# define USERDB                1       /* look in user database (requires NEWDB) */
+# ifndef NETINET
+#  define NETINET      1       /* include internet support */
 # endif
 
-/**********************************************************************
-**  0/1 Compilation options.
-**     #define these to 1 if they are available;
-**     #define them to 0 otherwise.
-**********************************************************************/
+# 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
 
-/*
-**  Most systems have symbolic links today, so default them on.  You
-**  can turn them off by #undef'ing this below.
-*/
+# ifndef XDEBUG
+#  define XDEBUG       1       /* enable extended debugging */
+# endif
 
-# define HASLSTAT      1       /* has lstat(2) call */
+# 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                   /* enable logging -- don't turn off */
+
+/**********************************************************************
+**  End of site-specific configuration.
+**********************************************************************/
+\f/*
 **  General "standard C" defines.
 **
 **     These may be undone later, to cope with systems that claim to
 # 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 */
+\f/**********************************************************************
 **  Operating system configuration.
 **
 **     Unless you are porting to a new OS, you shouldn't have to
 **     change these.
 **********************************************************************/
 
-/*
-**  Per-Operating System defines
-*/
-
-
 /*
 **  HP-UX -- tested for 8.07, 9.00, and 9.01.
+**
+**     If V4FS is defined, compile for HP-UX 10.0.
 */
 
-# ifdef __hpux
-/* avoid m_flags conflict between db.h & sys/sysmacros.h on HP 300 */
-# undef m_flags
+#ifdef __hpux
+               /* common definitions for HP-UX 9.x and 10.x */
+# undef m_flags                /* conflict between 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 HASSETREUID   1       /* has setreuid(2) call */
-# define setreuid(r, e)                setresuid(r, e, -1)
-# define LA_TYPE       LA_FLOAT
+# define HASFCHMOD     1       /* has fchmod(2) syscall */
+# define USESETEUID    1       /* has useable seteuid(2) call */
+# 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
-# define _PATH_UNIX    "/hp-ux"
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/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
 # define syslog                hard_syslog
 # ifdef __STDC__
-extern int     syslog(int, char *, ...);
+extern void    hard_syslog(int, char *, ...);
 # endif
+
+# ifdef V4FS
+               /* HP-UX 10.x */
+#  define _PATH_UNIX           "/stand/vmunix"
+#  define _PATH_VENDOR_CF      "/etc/mail/sendmail.cf"
+#  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"
+#  define _PATH_VENDOR_CF      "/usr/lib/sendmail.cf"
+#  ifndef IDENTPROTO
+#   define IDENTPROTO  0       /* TCP/IP implementation is broken */
+#  endif
 # endif
 
+#endif
+
 
 /*
 **  IBM AIX 3.x -- actually tested for 3.2.3
 */
 
-# ifdef _AIX3
+#ifdef _AIX3
+# include <paths.h>
+# include <sys/machine.h>      /* to get byte order */
 # 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 FORK          fork    /* no vfork primitive available */
-# undef  SETPROCTITLE          /* setproctitle confuses AIX */
+# define GIDSET_T      gid_t
 # define SFS_TYPE      SFS_STATFS      /* use <sys/statfs.h> statfs() impl */
-# endif
+# define SPT_PADCHAR   '\0'    /* pad process title with nulls */
+# define LA_TYPE       LA_INT
+# define LA_AVENRUN    "avenrun"
+#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.
+**
+**     IRIX64 changes from Mark R. Levinson <ml@cvdev.rochester.edu>.
+**     IRIX5 changes from Kari E. Hurtta <Kari.Hurtta@fmi.fi>.
 */
 
-# ifdef IRIX
+#if defined(IRIX64) || defined(IRIX5)
+# define IRIX
+#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 FORK          fork    /* no vfork primitive available */
-# define WAITUNION     1       /* use "union wait" as wait argument type */
 # 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 */
 # define LA_TYPE       LA_INT
+# ifdef IRIX64
+#  define NAMELISTMASK 0x7fffffffffffffff      /* mask for nlist() values */
+# else
+#  define NAMELISTMASK 0x7fffffff              /* mask for nlist() values */
 # endif
+# if defined(IRIX64) || defined(IRIX5)
+#  define ARGV_T       char *const *
+#  define HASSETRLIMIT 1       /* has setrlimit(2) syscall */
+#  define HASGETDTABLESIZE 1    /* has getdtablesize(2) syscall */
+# 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.2 (a.k.a. SunOS 5.2).
+**     Solaris 2.4 (a.k.a. SunOS 5.4).
 */
 
 #if defined(sun) && !defined(BSD)
@@ -207,10 +288,12 @@ extern int        syslog(int, char *, ...);
 # define HASINITGROUPS 1       /* has initgroups(3) call */
 # define HASUNAME      1       /* use System V uname(2) system call */
 # define HASGETUSERSHELL 1     /* DOES have getusershell(3) call in libc */
+# define HASFCHMOD     1       /* has fchmod(2) syscall */
+# define IP_SRCROUTE   1       /* can check IP source routing */
 # define LA_TYPE       LA_INT
 
 # ifdef SOLARIS_2_3
-#  define SOLARIS
+#  define SOLARIS      203     /* for back compat only -- use -DSOLARIS=203 */
 # endif
 
 # ifdef SOLARIS
@@ -219,18 +302,17 @@ extern int        syslog(int, char *, ...);
 #   define __svr4__            /* use all System V Releae 4 defines below */
 #  endif
 #  include <sys/time.h>
-#  define gethostbyname        solaris_gethostbyname   /* get working version */
-#  define gethostbyaddr        solaris_gethostbyaddr   /* get working version */
 #  define GIDSET_T     gid_t
 #  ifndef _PATH_UNIX
-#   define _PATH_UNIX  "/kernel/unix"
-#  endif
-#  ifndef _PATH_SENDMAILCF
-#   define _PATH_SENDMAILCF    "/etc/mail/sendmail.cf"
+#   define _PATH_UNIX          "/dev/ksyms"
 #  endif
+#  define _PATH_VENDOR_CF      "/etc/mail/sendmail.cf"
 #  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
@@ -242,12 +324,14 @@ extern int        syslog(int, char *, ...);
 #   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 <vfork.h>
 
 #  ifdef SUNOS403
                        /* special tweaking for SunOS 4.0.3 */
 #   include <malloc.h>
-#   define SYS5SIGNALS 1       /* SysV signal semantics -- reset on each sig */
+#   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
@@ -268,29 +352,41 @@ extern char               *getenv();
 /*
 **  DG/UX
 **
-**     Tested on 5.4.2
+**     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_SUBR
+# 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) */
 # ifndef IDENTPROTO
 #  define IDENTPROTO   0       /* TCP/IP implementation is broken */
 # endif
-# undef SETPROCTITLE
+# 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>
 
-# define inet_addr     dgux_inet_addr
+/* 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
 
 
@@ -307,11 +403,14 @@ extern long       dgux_inet_addr();
 # 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 */
-# define BROKEN_RES_SEARCH 1   /* res_search(unknown) returns h_errno=0 */
+# 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
@@ -320,19 +419,50 @@ extern long       dgux_inet_addr();
 # endif
 # define SFS_TYPE      SFS_MOUNT       /* use <sys/mount.h> statfs() impl */
 # ifndef IDENTPROTO
-#  define IDENTPROTO   0       /* TCP/IP implementation is broken */
+#  define IDENTPROTO   0       /* pre-4.4 TCP/IP implementation is broken */
+# endif
+#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 */
+# define FORK          fork    /* no vfork primitive available */
+# define _PATH_VENDOR_CF       "/var/adm/sendmail/sendmail.cf"
+# 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 */
+# define _PATH_VENDOR_CF       "/var/adm/sendmail/sendmail.cf"
+#endif
+
+
 /*
 **  OSF/1 (tested on Alpha)
 */
 
 #ifdef __osf__
 # define HASUNSETENV   1       /* has unsetenv(3) call */
-# define HASSETREUID   1       /* has setreuid(2) 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 */
 # ifndef HASFLOCK
 #  define HASFLOCK     1       /* has flock(2) call */
 # endif
@@ -350,11 +480,13 @@ extern long       dgux_inet_addr();
 
 #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 sleep         sleepX
 # define setpgid       setpgrp
 # ifndef LA_TYPE
@@ -366,9 +498,7 @@ typedef int         pid_t;
 #  undef WEXITSTATUS
 #  undef WIFEXITED
 # endif
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/etc/sendmail/sendmail.cf"
-# endif
+# define _PATH_VENDOR_CF       "/etc/sendmail/sendmail.cf"
 # ifndef _PATH_SENDMAILPID
 #  define _PATH_SENDMAILPID    "/etc/sendmail/sendmail.pid"
 # endif
@@ -381,14 +511,20 @@ typedef int               pid_t;
 **     See also BSD defines.
 */
 
-#ifdef BSD4_4
+#if defined(BSD4_4) && !defined(__bsdi__)
 # 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) */
 # 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 */
 # 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
 
 
@@ -400,52 +536,64 @@ typedef int               pid_t;
 #ifdef __bsdi__
 # 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 */
 # 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
 # if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199312
                        /* version 1.1 or later */
-#  define HASSETPROCTITLE 1    /* setproctitle is in libc */
-#  undef SETPROCTITLE          /* so don't redefine it in conf.c */
+#  undef SPT_TYPE
+#  define SPT_TYPE     SPT_BUILTIN     /* setproctitle is in libc */
 # else
                        /* version 1.0 or earlier */
 #  ifndef OLD_NEWDB
 #   define OLD_NEWDB   1       /* old version of newdb library */
 #  endif
+#  define SPT_PADCHAR  '\0'    /* pad process title with nulls */
 # endif
 #endif
 
 
 
 /*
-**  386BSD / FreeBSD 1.0E / NetBSD (all architectures, all versions)
+**  FreeBSD / NetBSD (all architectures, all versions)
 **
-**  4.3BSD clone, closer to 4.4BSD
+**  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(__386BSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
+#if defined(__FreeBSD__) || defined(__NetBSD__)
 # define HASUNSETENV   1       /* has unsetenv(3) call */
 # define HASSETSID     1       /* has the setsid(2) POSIX syscall */
-#if defined(__FreeBSD__) || defined(__NetBSD__)
-# define HASUNAME       1 
-#endif
-#if defined(__NetBSD__) && ((NetBSD > 199307) || (NetBSD0_9 > 1))
-# undef SETPROCTITLE
-# define HASSETPROCTITLE
-# define setreuid(r,e) __setreuid(r,e)
-# define GIDSET_T       gid_t
-#endif
+# 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 */
 # 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 GIDSET_T      gid_t
 # 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 */
+#  define setreuid     __setreuid
+# endif
 #endif
 
 
@@ -472,9 +620,7 @@ typedef int         pid_t;
 # undef HASSETVBUF             /* don't actually have setvbuf(3) */
 # undef WEXITSTATUS
 # undef WIFEXITED
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/lib/sendmail.cf"
-# endif
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
 # ifndef _PATH_SENDMAILPID
 #  define _PATH_SENDMAILPID    "/etc/sendmail.pid"
 # endif
@@ -498,9 +644,7 @@ typedef int         pid_t;
 # ifndef LA_TYPE
 #  define LA_TYPE      LA_FLOAT
 # endif
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/lib/sendmail.cf"
-# endif
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
 # ifndef IDENTPROTO
 #  define IDENTPROTO   0       /* TCP/IP implementation is broken */
 # endif
@@ -522,25 +666,86 @@ extern int                errno;
 #ifdef _SCO_unix_4_2
 # define _SCO_unix_
 # define HASSETREUID   1       /* has setreuid(2) call */
-# define NEEDFSYNC     1       /* needs the fsync(2) call stub */
-# define _PATH_UNIX    "/unix"
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/lib/sendmail.cf"
-# endif
+# define _PATH_UNIX            "/unix"
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
 # ifndef _PATH_SENDMAILPID
 #  define _PATH_SENDMAILPID    "/etc/sendmail.pid"
 # endif
 #endif
 
 #ifdef _SCO_unix_
+# include <sys/stream.h>       /* needed for IP_SRCROUTE */
+# 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 NOFTRUNCATE   0       /* does not have ftruncate(3) call */
+# define NEEDFSYNC     1       /* needs the fsync(2) call stub */
+# define FORK          fork
+# define MAXPATHLEN    PATHSIZE
+# define LA_TYPE       LA_SHORT
+# 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 NETUNIX       0       /* no unix domain socket support */
+#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 FORK          fork
+# 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"
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
+# ifndef _PATH_SENDMAILPID
+#  define _PATH_SENDMAILPID    "/etc/sendmail.pid"
+# endif
+
+#endif
+
+
+/*
+**  Altos System V.
+**     Contributed by Tim Rice <timr@crl.com>.
+*/
+
+#ifdef ALTOS_SYS_V
 # 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 FORK          fork
 # define MAXPATHLEN    PATHSIZE
 # define LA_TYPE       LA_SHORT
 # define SFS_TYPE      SFS_STATFS      /* use <sys/statfs.h> statfs() impl */
-# undef NETUNIX                        /* no unix domain socket support */
+# define SFS_BAVAIL    f_bfree         /* alternate field name */
+# define TZ_TYPE       TZ_TM_NAME      /* use tm->tm_name */
+# 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;
 #endif
 
 
@@ -556,11 +761,10 @@ extern int                errno;
 # define HASUNAME      1       /* use System V uname(2) system call */
 # define HASSETSID     1       /* has POSIX setsid(2) call */
 # define NEEDGETOPT    1       /* 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_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/lib/sendmail.cf"
-# endif
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
 # ifndef S_IREAD
 #  define S_IREAD      _S_IREAD
 #  define S_IWRITE     _S_IWRITE
@@ -589,6 +793,9 @@ extern int          errno;
 # 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"
@@ -603,6 +810,8 @@ typedef int         (*sigfunc_t)();
 extern char            *getenv();
 extern void            *malloc();
 
+# include <sys/time.h>
+
 #endif
 
 
@@ -612,13 +821,13 @@ extern void               *malloc();
 **  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:     [03/02/94 @ 05:34 PM (Wednesday)]
-**     sendmail 8.6.6.b9       named 4.9.2-931205-p1   db-1.73
-**     gcc 2.5.8               libc.so.4.5.19
-**     slackware 1.1.2         linux 0.99.15
+**  Last compiled against:     [09/06/95 @ 10:20:58 AM (Wednesday)]
+**     sendmail 8.7-b14        named 4.9.3-beta17      db-1.85
+**     gcc 2.7.0               libc-5.2.7              linux 1.2.13
 */
 
 #ifdef __linux__
@@ -626,12 +835,24 @@ extern void               *malloc();
 # 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 */
+# ifndef HASFLOCK
+#  define HASFLOCK     0       /* flock(2) is broken after 0.99.13 */
+# endif
 # ifndef LA_TYPE
 #  define LA_TYPE      LA_PROCSTR
 # endif
 # define SFS_TYPE      SFS_VFS         /* use <sys/vfs.h> statfs() impl */
+# 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
@@ -662,20 +883,28 @@ extern void               *malloc();
 #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 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       /* we have setvbuf(3) in libc */
 # define SIGFUNC_DEFINED       /* sigfunc_t already defined */
 # ifndef IDENTPROTO
 #  define IDENTPROTO   0       /* TCP/IP implementation is broken */
 # endif
 # define FORK          fork
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/lib/sendmail.cf"
-# endif
 # ifndef LA_TYPE
-#  define LA_TYPE      LA_ZERO
+#  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
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
 # undef WIFEXITED
 # undef WEXITSTATUS
 #endif
@@ -688,7 +917,6 @@ extern void         *malloc();
 */
 
 #ifdef UMAXV
-# include <limits.h>
 # 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 */
@@ -755,11 +983,9 @@ typedef int                pid_t;
 # endif
 
 # ifndef _PATH_UNIX
-#  define _PATH_UNIX   "/dynix"
-# endif
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/lib/sendmail.cf"
+#  define _PATH_UNIX           "/dynix"
 # endif
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
 
 #endif
 
@@ -781,13 +1007,11 @@ typedef int              pid_t;
 # define GIDSET_T      gid_t
 # define LA_TYPE       LA_INT
 # define SFS_TYPE      SFS_STATFS      /* use <sys/statfs.h> statfs() impl */
-# undef SETPROCTITLE
+# define SPT_TYPE      SPT_NONE        /* don't use setproctitle */
 # ifndef IDENTPROTO
 #  define IDENTPROTO   0       /* TCP/IP implementation is broken */
 # endif
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/lib/sendmail.cf"
-# endif
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
 # ifndef _PATH_SENDMAILPID
 #  define _PATH_SENDMAILPID    "/etc/sendmail.pid"
 # endif
@@ -806,6 +1030,7 @@ typedef int                pid_t;
 # 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
 
 
@@ -814,19 +1039,20 @@ typedef int              pid_t;
 **
 **  From Todd Martin <tmartint@tus.ssi1.com> & Don Lewis <gdonl@gv.ssi1.com>
 **
-**  15 Jan 1994
+**  15 Jan 1994; updated 2 Aug 1995
 **
 */
 
 #ifdef apollo
 # define HASSETREUID   1       /* has setreuid(2) call */
 # define HASINITGROUPS 1       /* has initgroups(2) call */
-# undef  SETPROCTITLE
+# 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 */
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/lib/sendmail.cf"
-# endif
+# define SFS_BAVAIL    f_bfree         /* alternate field name */
+# define TZ_TYPE       TZ_TZNAME
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
 # ifndef _PATH_SENDMAILPID
 #  define _PATH_SENDMAILPID    "/etc/sendmail.pid"
 # endif
@@ -840,26 +1066,37 @@ typedef int              pid_t;
 
 
 /*
-**  UnixWare
+**  UnixWare 2.x
+*/
+
+#ifdef UNIXWARE2
+# define UNIXWARE      1
+# define HASSNPRINTF   1       /* has snprintf(3) and vsnprintf(3) */
+#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
-# ifndef HASGETUSERSHELL
-#  define HASGETUSERSHELL 0    /* does not have getusershell(3) call */
-# endif
-# define GIDSET_T              int
-# define SLEEP_T               int
+# 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_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/ucblib/sendmail.cf"
-# endif
+# define _PATH_VENDOR_CF       "/usr/ucblib/sendmail.cf"
 # ifndef _PATH_SENDMAILPID
 #  define _PATH_SENDMAILPID    "/usr/ucblib/sendmail.pid"
 # endif
@@ -889,24 +1126,257 @@ typedef int             pid_t;
 /*
 **  NCR 3000 Series (SysVr4)
 **
-**     From From: Kevin Darcy <kevin@tech.mis.cfc.com>.
+**     From Kevin Darcy <kevin@tech.mis.cfc.com>.
 */
 
 #ifdef NCR3000
+# include <sys/sockio.h>
 # define __svr4__
+# define IP_SRCROUTE   0       /* Something is broken with getsockopt() */
 # undef BSD
 # define LA_AVENRUN    "avenrun"
+# define SYSLOG_BUFSIZE        1024
 #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
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
+# 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 db.h & sys/sysmacros.h on HIUX 3050 */
+# undef m_flags
+
+# ifdef __STDC__
+extern int     syslog(int, char *, ...);
+# 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 */
+# define HASSIGSETMASK 0       /* does not have sigsetmask(2) 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"
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
+#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
+typedef int            pid_t;
+typedef int            (*sigfunc_t)();
+#  define SIGFUNC_DEFINED
+
+# 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"
+#  define _PATH_VENDOR_CF      "/etc/mail/sendmail.cf"
+#  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
+extern char    *getenv();
+extern int     errno;
+# define _PATH_VENDOR_CF       "/usr/lib/sendmail.cf"
+#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>.
+*/
+
+#ifdef UXPDS
+# define __svr4__
+# define HASGETUSERSHELL       1
+# define HASFLOCK              0
+# define _PATH_UNIX            "/stand/unix"
+# define _PATH_VENDOR_CF       "/etc/mail/sendmail.cf"
+# ifndef _PATH_SENDMAILPID
+#  define _PATH_SENDMAILPID    "/etc/mail/sendmail.pid"
+# endif
+#endif
+
 
 
 /**********************************************************************
 **  End of Per-Operating System defines
 **********************************************************************/
-
-/**********************************************************************
+\f/**********************************************************************
 **  More general defines
 **********************************************************************/
 
@@ -914,26 +1384,39 @@ typedef int              pid_t;
 #ifdef BSD
 # define HASGETDTABLESIZE 1    /* has getdtablesize(2) call */
 # define HASSETREUID   1       /* has setreuid(2) call */
-# define HASINITGROUPS 1       /* has initgroups(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 HASSETREUID   1       /* has seteuid(2) call & working saved uids */
+# 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
-# define setreuid(r, e)        seteuid(e)
 
 # ifndef _PATH_UNIX
 #  define _PATH_UNIX           "/unix"
 # endif
-# ifndef _PATH_SENDMAILCF
-#  define _PATH_SENDMAILCF     "/usr/ucblib/sendmail.cf"
+# ifndef _PATH_VENDOR_CF
+#  define _PATH_VENDOR_CF      "/usr/ucblib/sendmail.cf"
 # endif
 # ifndef _PATH_SENDMAILPID
 #  define _PATH_SENDMAILPID    "/usr/ucblib/sendmail.pid"
@@ -941,6 +1424,13 @@ typedef int               pid_t;
 # ifndef SYSLOG_BUFSIZE
 #  define SYSLOG_BUFSIZE       128
 # endif
+# ifndef SFS_TYPE
+#  define SFS_TYPE             SFS_STATVFS
+# endif
+
+# define jmp_buf               sigjmp_buf
+# define setjmp(env)           sigsetjmp(env, 1)
+# define longjmp(env, val)     siglongjmp(env, val)
 #endif
 
 /* general System V defines */
@@ -949,12 +1439,22 @@ typedef int              pid_t;
 # 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
-#  define LA_TYPE      LA_INT          /* assume integer load average */
+#  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)))
@@ -964,25 +1464,31 @@ typedef int              pid_t;
 #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
 #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.
+\f/*
+**  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).
 */
 
-#ifndef GIDSET_T
-# define GIDSET_T      int
+#ifdef titan
+# undef HASINITGROUPS          /* doesn't have initgroups(3) call */
 #endif
 
-/*
-**  Tweaking for systems that (for example) claim to be BSD but
-**  don't have all the standard BSD routines (boo hiss).
-*/
+#ifdef _CRAYCOM
+# undef HASSETSID              /* despite POSIX claim, doesn't have setsid */
+#endif
 
-#ifdef titan
-# undef HASINITGROUPS          /* doesn't have initgroups(3) call */
+#ifdef ISC_UNIX
+# undef bcopy                  /* despite SystemV claim, uses BSD bcopy */
+#endif
+
+#ifdef ALTOS_SYS_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
 
 
@@ -1001,20 +1507,77 @@ typedef int             pid_t;
 # 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 OLD_NEWDB
 # define OLD_NEWDB     0       /* assume newer version of newdb */
 #endif
 
+/* heuristic setting of HASSETSIGMASK; can override above */
+#ifndef HASSIGSETMASK
+# ifdef SIGVTALRM
+#  define HASSETSIGMASK        1
+# else
+#  define HASSETSIGMASK        0
+# endif
+#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 SIZE_T
+# define SIZE_T                size_t
+#endif
+
+#ifndef ARGV_T
+# define ARGV_T                char **
+#endif
+\f/**********************************************************************
 **  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
@@ -1027,11 +1590,14 @@ typedef int             pid_t;
 #if !defined(S_ISLNK) && defined(S_IFLNK)
 # define S_ISLNK(foo)  ((foo & S_IFMT) == S_IFLNK)
 #endif
+#ifndef S_IWUSR
+# define S_IWUSR               0200
+#endif
 #ifndef S_IWGRP
-#define S_IWGRP                020
+# define S_IWGRP               0020
 #endif
 #ifndef S_IWOTH
-#define S_IWOTH                002
+# define S_IWOTH               0002
 #endif
 
 /*
@@ -1069,11 +1635,45 @@ typedef int             pid_t;
 # include "cdefs.h"
 #endif
 
+#if NAMED_BIND
+# include <arpa/nameser.h>
+# ifdef __svr4__
+#  ifdef NOERROR
+#   undef NOERROR              /* avoid compiler conflict with stream.h */
+#  endif
+# endif
+# ifndef __ksr__
+extern int h_errno;
+# endif
+#endif
+
+/*
+**  The size of an IP address -- can't use sizeof because of problems
+**  on Crays, where everything is 64 bits.  This will break if/when
+**  IP addresses are expanded to eight bytes.
+*/
+
+#ifndef INADDRSZ
+# define INADDRSZ      4
+#endif
+
+/*
+**  The size of various known types -- for reading network protocols.
+**  Again, we can't use sizeof because of compiler randomness.
+*/
+
+#ifndef INT16SZ
+# define INT16SZ       2
+#endif
+#ifndef INT32SZ
+# define INT32SZ       4
+#endif
+
 /*
 **  Do some required dependencies
 */
 
-#if defined(NETINET) || defined(NETISO)
+#if NETINET || NETISO
 # define SMTP          1       /* enable user and server SMTP */
 # define QUEUE         1       /* enable queueing */
 # define DAEMON                1       /* include the daemon (requires IPC & SMTP) */
@@ -1115,7 +1715,7 @@ struct utsname
 };
 #endif /* HASUNAME */
 
-#if !defined(MAXHOSTNAMELEN) && !defined(_SCO_unix_)
+#if !defined(MAXHOSTNAMELEN) && !defined(_SCO_unix_) && !defined(NonStop_UX_BXX) && !defined(ALTOS_SYS_V)
 # define MAXHOSTNAMELEN        256
 #endif
 
@@ -1124,15 +1724,15 @@ struct utsname
 #endif
 
 #ifndef STDIN_FILENO
-#define STDIN_FILENO   0
+# define STDIN_FILENO  0
 #endif
 
 #ifndef STDOUT_FILENO
-#define STDOUT_FILENO  1
+# define STDOUT_FILENO 1
 #endif
 
 #ifndef STDERR_FILENO
-#define STDERR_FILENO  2
+# define STDERR_FILENO 2
 #endif
 
 #ifndef LOCK_SH
@@ -1142,6 +1742,12 @@ struct utsname
 # define LOCK_UN       0x08    /* unlock */
 #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
@@ -1169,13 +1775,19 @@ typedef void            (*sigfunc_t) __P((int));
 */
 
 #ifndef TOBUFSIZE
-# if (SYSLOG_BUFSIZE) > 512
-#  define TOBUFSIZE    (SYSLOG_BUFSIZE - 256)
+# if (SYSLOG_BUFSIZE) > 768
+#  define TOBUFSIZE    (SYSLOG_BUFSIZE - 512)
 # else
 #  define TOBUFSIZE    256
 # endif
 #endif
 
+/* TOBUFSIZE must never be permitted to exceed MAXLINE - 128 */
+#if TOBUFSIZE > (MAXLINE - 128)
+# undef TOBUFSIZE
+# define TOBUFSIZE     (MAXLINE - 128)
+#endif
+
 /*
 **  Size of prescan buffer.
 **     Despite comments in the _sendmail_ book, this probably should
@@ -1189,9 +1801,9 @@ typedef void              (*sigfunc_t) __P((int));
 # endif
 
 /*
-**  If we are going to link scanf anyway, use it in readcf
+**  Default to using scanf in readcf.
 */
 
-#if !defined(HASUNAME) && !defined(SCANF)
+#ifndef SCANF
 # define SCANF         1
 #endif
index 109c30f..cb5792f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)convtime.c 8.1 (Berkeley) 6/7/93";
+static char sccsid[] = "@(#)convtime.c 8.4 (Berkeley) 5/19/95";
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -80,6 +80,11 @@ convtime(p, units)
                        c = units;
                        p--;
                }
+               else if (strchr("wdhms", c) == NULL)
+               {
+                       usrerr("Invalid time unit `%c'", c);
+                       c = units;
+               }
                switch (c)
                {
                  case 'w':             /* weeks */
index 9753d4c..e6ffb52 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
 
 #ifndef lint
 #ifdef DAEMON
-static char sccsid[] = "@(#)daemon.c   8.48.1.5 (Berkeley) 3/28/95 (with daemon mode)";
+static char sccsid[] = "@(#)daemon.c   8.119 (Berkeley) 11/29/95 (with daemon mode)";
 #else
-static char sccsid[] = "@(#)daemon.c   8.48.1.5 (Berkeley) 3/28/95 (without daemon mode)";
+static char sccsid[] = "@(#)daemon.c   8.119 (Berkeley) 11/29/95 (without daemon mode)";
 #endif
 #endif /* not lint */
 
 #ifdef DAEMON
 
-# include <netdb.h>
 # include <arpa/inet.h>
 
 #if NAMED_BIND
-# include <arpa/nameser.h>
 # include <resolv.h>
+# ifndef NO_DATA
+#  define NO_DATA      NO_ADDRESS
+# endif
+#endif
+
+#if IP_SRCROUTE
+# include <netinet/in_systm.h>
+# include <netinet/ip.h>
+# include <netinet/ip_var.h>
 #endif
 
 /*
@@ -106,13 +113,14 @@ 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()
 {
        int t;
        bool refusingconnections = TRUE;
        FILE *pidf;
        int socksize;
-#ifdef XDEBUG
+#if XDEBUG
        bool j_has_dot;
 #endif
        extern void reapchild();
@@ -167,11 +175,11 @@ getrequests()
                fclose(pidf);
        }
 
-#ifdef XDEBUG
+#if XDEBUG
        {
                char jbuf[MAXHOSTNAMELEN];
 
-               expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
+               expand("\201j", jbuf, sizeof jbuf, CurEnv);
                j_has_dot = strchr(jbuf, '.') != NULL;
        }
 #endif
@@ -184,6 +192,7 @@ getrequests()
                register int pid;
                auto int lotherend;
                extern bool refuseconnections();
+               extern int getla();
 
                /* see if we are rejecting connections */
                CurrentLA = getla();
@@ -196,29 +205,24 @@ getrequests()
                                DaemonSocket = -1;
                        }
                        refusingconnections = TRUE;
-                       setproctitle("rejecting connections: load average: %d",
-                               CurrentLA);
                        sleep(15);
                        continue;
                }
 
+               /* arrange to (re)open the socket if necessary */
                if (refusingconnections)
                {
-                       /* start listening again */
                        (void) opendaemonsocket(FALSE);
-                       setproctitle("accepting connections");
                        refusingconnections = FALSE;
                }
 
-#ifdef XDEBUG
+#if XDEBUG
                /* check for disaster */
                {
-                       register STAB *s;
                        char jbuf[MAXHOSTNAMELEN];
 
-                       expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
-                       if ((s = stab(jbuf, ST_CLASS, ST_FIND)) == NULL ||
-                           !bitnset('w', s->s_class))
+                       expand("\201j", jbuf, sizeof jbuf, CurEnv);
+                       if (!wordinclass(jbuf, 'w'))
                        {
                                dumpstate("daemon lost $j");
                                syslog(LOG_ALERT, "daemon process doesn't have $j in $=w; see syslog");
@@ -234,6 +238,7 @@ getrequests()
 #endif
 
                /* wait for a connection */
+               setproctitle("accepting connections");
                do
                {
                        errno = 0;
@@ -244,6 +249,11 @@ getrequests()
                if (t < 0)
                {
                        syserr("getrequests: accept");
+
+                       /* arrange to re-open the socket next time around */
+                       (void) close(DaemonSocket);
+                       DaemonSocket = -1;
+                       refusingconnections = TRUE;
                        sleep(5);
                        continue;
                }
@@ -268,6 +278,8 @@ getrequests()
                {
                        char *p;
                        extern char *hostnamebyanyaddr();
+                       extern void intsig();
+                       FILE *inchannel, *outchannel;
 
                        /*
                        **  CHILD -- return to caller.
@@ -276,34 +288,31 @@ getrequests()
                        */
 
                        (void) setsignal(SIGCHLD, SIG_DFL);
-                       DisConnected = FALSE;
+                       (void) setsignal(SIGHUP, intsig);
+                       (void) close(DaemonSocket);
 
                        setproctitle("startup with %s",
                                anynet_ntoa(&RealHostAddr));
 
                        /* determine host name */
                        p = hostnamebyanyaddr(&RealHostAddr);
+                       if (strlen(p) > MAXNAME)
+                               p[MAXNAME] = '\0';
                        RealHostName = newstr(p);
                        setproctitle("startup with %s", p);
 
-#ifdef LOG
-                       if (LogLevel > 11)
-                       {
-                               /* log connection information */
-                               syslog(LOG_INFO, "connect from %s (%s)",
-                                       RealHostName, anynet_ntoa(&RealHostAddr));
-                       }
-#endif
-
-                       (void) close(DaemonSocket);
-                       if ((InChannel = fdopen(t, "r")) == NULL ||
+                       if ((inchannel = fdopen(t, "r")) == NULL ||
                            (t = dup(t)) < 0 ||
-                           (OutChannel = fdopen(t, "w")) == NULL)
+                           (outchannel = fdopen(t, "w")) == NULL)
                        {
                                syserr("cannot open SMTP server channel, fd=%d", t);
                                exit(0);
                        }
 
+                       InChannel = inchannel;
+                       OutChannel = outchannel;
+                       DisConnected = FALSE;
+
                        /* should we check for illegal connection here? XXX */
 #ifdef XLA
                        if (!xla_host_ok(RealHostName))
@@ -318,6 +327,8 @@ getrequests()
                        return;
                }
 
+               CurChildren++;
+
                /* close the port so that others will hang (for a while) */
                (void) close(t);
        }
@@ -347,7 +358,7 @@ opendaemonsocket(firsttime)
        bool firsttime;
 {
        int on = 1;
-       int socksize;
+       int socksize = 0;
        int ntries = 0;
        int saveerrno;
 
@@ -363,7 +374,6 @@ opendaemonsocket(firsttime)
                        DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0);
                        if (DaemonSocket < 0)
                        {
-                               /* probably another daemon already */
                                saveerrno = errno;
                                syserr("opendaemonsocket: can't create server SMTP socket");
                          severe:
@@ -393,19 +403,19 @@ opendaemonsocket(firsttime)
                                               SO_RCVBUF,
                                               (char *) &TcpRcvBufferSize,
                                               sizeof(TcpRcvBufferSize)) < 0)
-                                       syserr("getrequests: setsockopt(SO_RCVBUF)");
+                                       syserr("opendaemonsocket: setsockopt(SO_RCVBUF)");
                        }
 #endif
 
                        switch (DaemonAddr.sa.sa_family)
                        {
-# ifdef NETINET
+# if NETINET
                          case AF_INET:
                                socksize = sizeof DaemonAddr.sin;
                                break;
 # endif
 
-# ifdef NETISO
+# if NETISO
                          case AF_ISO:
                                socksize = sizeof DaemonAddr.siso;
                                break;
@@ -418,8 +428,9 @@ opendaemonsocket(firsttime)
 
                        if (bind(DaemonSocket, &DaemonAddr.sa, socksize) < 0)
                        {
+                               /* probably another daemon already */
                                saveerrno = errno;
-                               syserr("getrequests: cannot bind");
+                               syserr("opendaemonsocket: cannot bind");
                                (void) close(DaemonSocket);
                                goto severe;
                        }
@@ -427,12 +438,13 @@ opendaemonsocket(firsttime)
                if (!firsttime && listen(DaemonSocket, ListenQueueSize) < 0)
                {
                        saveerrno = errno;
-                       syserr("getrequests: cannot listen");
+                       syserr("opendaemonsocket: cannot listen");
                        (void) close(DaemonSocket);
                        goto severe;
                }
                return socksize;
        } while (ntries++ < MAXOPENTRIES && transienterror(saveerrno));
+       syserr("!opendaemonsocket: server SMTP socket wedged: exiting");
        finis();
 }
 \f/*
@@ -448,6 +460,7 @@ opendaemonsocket(firsttime)
 **             releases any resources used by the passive daemon.
 */
 
+void
 clrdaemon()
 {
        if (DaemonSocket >= 0)
@@ -464,6 +477,7 @@ clrdaemon()
 **             none.
 */
 
+void
 setdaemonoptions(p)
        register char *p;
 {
@@ -488,25 +502,27 @@ setdaemonoptions(p)
                        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);
-#ifdef NETINET
+#if NETINET
                        else if (strcasecmp(v, "inet") == 0)
                                DaemonAddr.sa.sa_family = AF_INET;
 #endif
-#ifdef NETISO
+#if NETISO
                        else if (strcasecmp(v, "iso") == 0)
                                DaemonAddr.sa.sa_family = AF_ISO;
 #endif
-#ifdef NETNS
+#if NETNS
                        else if (strcasecmp(v, "ns") == 0)
                                DaemonAddr.sa.sa_family = AF_NS;
 #endif
-#ifdef NETX25
+#if NETX25
                        else if (strcasecmp(v, "x.25") == 0)
                                DaemonAddr.sa.sa_family = AF_CCITT;
 #endif
@@ -517,10 +533,10 @@ setdaemonoptions(p)
                  case 'A':             /* address */
                        switch (DaemonAddr.sa.sa_family)
                        {
-#ifdef NETINET
+#if NETINET
                          case AF_INET:
                                if (isascii(*v) && isdigit(*v))
-                                       DaemonAddr.sin.sin_addr.s_addr = inet_network(v);
+                                       DaemonAddr.sin.sin_addr.s_addr = htonl(inet_network(v));
                                else
                                {
                                        register struct netent *np;
@@ -546,7 +562,7 @@ setdaemonoptions(p)
                        {
                                short port;
 
-#ifdef NETINET
+#if NETINET
                          case AF_INET:
                                if (isascii(*v) && isdigit(*v))
                                        DaemonAddr.sin.sin_port = htons(atoi(v));
@@ -563,7 +579,7 @@ setdaemonoptions(p)
                                break;
 #endif
 
-#ifdef NETISO
+#if NETISO
                          case AF_ISO:
                                /* assume two byte transport selector */
                                if (isascii(*v) && isdigit(*v))
@@ -600,6 +616,9 @@ setdaemonoptions(p)
                  case 'R':             /* receive buffer size */
                        TcpRcvBufferSize = atoi(v);
                        break;
+
+                 default:
+                       syserr("554 DaemonPortOptions parameter \"%s\" unknown", f);
                }
        }
 }
@@ -622,6 +641,15 @@ setdaemonoptions(p)
 **             none.
 */
 
+static jmp_buf CtxConnectTimeout;
+
+static void
+connecttimeout()
+{
+       errno = ETIMEDOUT;
+       longjmp(CtxConnectTimeout, 1);
+}
+
 SOCKADDR       CurHostAddr;            /* address of current host */
 
 int
@@ -631,14 +659,14 @@ makeconnection(host, port, mci, usesecureport)
        register MCI *mci;
        bool usesecureport;
 {
-       register int i, s;
+       register int i = 0;
+       register int s;
        register struct hostent *hp = (struct hostent *)NULL;
        SOCKADDR addr;
        int sav_errno;
        int addrlen;
-#if NAMED_BIND
-       extern int h_errno;
-#endif
+       bool firstconnect;
+       EVENT *ev;
 
        /*
        **  Set up the address for the mailer.
@@ -660,17 +688,25 @@ makeconnection(host, port, mci, usesecureport)
                if (p != NULL)
                {
                        *p = '\0';
-#ifdef NETINET
+#if NETINET
                        if (inet_aton(&host[1], &addr.sin.sin_addr) == 0)
 #endif
                        {
                                /* try it as a host name (avoid MX lookup) */
-                               hp = gethostbyname(&host[1]);
+                               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 = gethostbyname(&host[1]);
+                                       hp = sm_gethostbyname(&host[1]);
                                        p[-1] = '.';
+#if NAMED_BIND
+                                       _res.options = oldopts;
+#endif
                                }
                                *p = ']';
                                goto gothostent;
@@ -680,9 +716,10 @@ makeconnection(host, port, mci, usesecureport)
                if (p == NULL)
                {
                        usrerr("553 Invalid numeric domain spec \"%s\"", host);
+                       mci->mci_status = "5.1.2";
                        return (EX_NOHOST);
                }
-#ifdef NETINET
+#if NETINET
                addr.sin.sin_family = AF_INET;          /*XXX*/
 #endif
        }
@@ -690,34 +727,43 @@ makeconnection(host, port, mci, usesecureport)
        {
                register char *p = &host[strlen(host) - 1];
 
-               hp = gethostbyname(host);
+               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 = gethostbyname(host);
+                       hp = sm_gethostbyname(host);
                        *p = '.';
+#if NAMED_BIND
+                       _res.options = oldopts;
+#endif
                }
 gothostent:
                if (hp == NULL)
                {
 #if NAMED_BIND
-                       if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
-                               return (EX_TEMPFAIL);
-
-                       /* if name server is specified, assume temp fail */
-                       if (errno == ECONNREFUSED && UseNameServer)
+                       /* check for name server timeouts */
+                       if (errno == ETIMEDOUT || h_errno == TRY_AGAIN ||
+                           (errno == ECONNREFUSED && UseNameServer))
+                       {
+                               mci->mci_status = "4.4.3";
                                return (EX_TEMPFAIL);
+                       }
 #endif
                        return (EX_NOHOST);
                }
                addr.sa.sa_family = hp->h_addrtype;
                switch (hp->h_addrtype)
                {
-#ifdef NETINET
+#if NETINET
                  case AF_INET:
                        bcopy(hp->h_addr,
                                &addr.sin.sin_addr,
-                               sizeof addr.sin.sin_addr);
+                               INADDRSZ);
                        break;
 #endif
 
@@ -734,15 +780,16 @@ gothostent:
        **  Determine the port number.
        */
 
-       if (port != 0)
-               port = htons(port);
-       else
+       if (port == 0)
        {
                register struct servent *sp = getservbyname("smtp", "tcp");
 
                if (sp == NULL)
                {
-                       syserr("554 makeconnection: service \"smtp\" unknown");
+#ifdef LOG
+                       if (LogLevel > 2)
+                               syslog(LOG_ERR, "makeconnection: service \"smtp\" unknown");
+#endif
                        port = htons(25);
                }
                else
@@ -751,14 +798,14 @@ gothostent:
 
        switch (addr.sa.sa_family)
        {
-#ifdef NETINET
+#if NETINET
          case AF_INET:
                addr.sin.sin_port = port;
                addrlen = sizeof (struct sockaddr_in);
                break;
 #endif
 
-#ifdef NETISO
+#if NETISO
          case AF_ISO:
                /* assume two byte transport selector */
                bcopy((char *) &port, TSEL((struct sockaddr_iso *) &addr), 2);
@@ -781,6 +828,7 @@ gothostent:
                return EX_TEMPFAIL;
 #endif
 
+       firstconnect = TRUE;
        for (;;)
        {
                if (tTd(16, 1))
@@ -803,7 +851,7 @@ gothostent:
                if (s < 0)
                {
                        sav_errno = errno;
-                       syserr("makeconnection: no socket");
+                       syserr("makeconnection: cannot create socket");
                        goto failure;
                }
 
@@ -830,24 +878,54 @@ gothostent:
                if (CurEnv->e_xfp != NULL)
                        (void) fflush(CurEnv->e_xfp);           /* for debugging */
                errno = 0;                                      /* for debugging */
-               if (connect(s, (struct sockaddr *) &addr, addrlen) >= 0)
-                       break;
 
-               /* couldn't connect.... figure out why */
+               /*
+               **  Linux seems to hang in connect for 90 minutes (!!!).
+               **  Time out the connect to avoid this problem.
+               */
+
+               if (setjmp(CtxConnectTimeout) == 0)
+               {
+                       if (TimeOuts.to_connect == 0)
+                               ev = NULL;
+                       else
+                               ev = setevent(TimeOuts.to_connect, connecttimeout, 0);
+                       if (connect(s, (struct sockaddr *) &addr, addrlen) >= 0)
+                       {
+                               if (ev != NULL)
+                                       clrevent(ev);
+                               break;
+                       }
+               }
                sav_errno = errno;
+               if (ev != NULL)
+                       clrevent(ev);
+
+               /* 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 (hp && hp->h_addr_list[i])
+               if (hp != NULL && hp->h_addr_list[i])
                {
                        if (tTd(16, 1))
                                printf("Connect failed (%s); trying new address....\n",
                                        errstring(sav_errno));
                        switch (addr.sa.sa_family)
                        {
-#ifdef NETINET
+#if NETINET
                          case AF_INET:
                                bcopy(hp->h_addr_list[i++],
                                      &addr.sin.sin_addr,
-                                     sizeof addr.sin.sin_addr);
+                                     INADDRSZ);
                                break;
 #endif
 
@@ -899,61 +977,77 @@ gothostent:
 **             Adds numeric codes to $=w.
 */
 
-char **
+struct hostent *
 myhostname(hostbuf, size)
        char hostbuf[];
        int size;
 {
        register struct hostent *hp;
-       extern struct hostent *gethostbyname();
+       extern bool getcanonname();
 
        if (gethostname(hostbuf, size) < 0)
        {
                (void) strcpy(hostbuf, "localhost");
        }
-       hp = gethostbyname(hostbuf);
+       hp = sm_gethostbyname(hostbuf);
        if (hp == NULL)
+               return NULL;
+       if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL)
        {
-               syserr("!My host name (%s) does not seem to exist!", hostbuf);
+               (void) strncpy(hostbuf, hp->h_name, size - 1);
+               hostbuf[size - 1] = '\0';
        }
-       (void) strncpy(hostbuf, hp->h_name, size - 1);
-       hostbuf[size - 1] = '\0';
 
-#if NAMED_BIND
-       /* if still no dot, try DNS directly (i.e., avoid NIS problems) */
+       /*
+       **  If there is still no dot in the name, try looking for a
+       **  dotted alias.
+       */
+
        if (strchr(hostbuf, '.') == NULL)
        {
-               extern bool getcanonname();
-               extern int h_errno;
+               char **ha;
 
-               /* try twice in case name server not yet started up */
-               if (!getcanonname(hostbuf, size, TRUE) &&
-                   UseNameServer &&
-                   (h_errno != TRY_AGAIN ||
-                    (sleep(30), !getcanonname(hostbuf, size, TRUE))))
+               for (ha = hp->h_aliases; *ha != NULL; ha++)
                {
-                       errno = h_errno + E_DNSBASE;
-                       syserr("!My host name (%s) not known to DNS",
-                               hostbuf);
+                       if (strchr(*ha, '.') != NULL)
+                       {
+                               (void) strncpy(hostbuf, *ha, size - 1);
+                               hostbuf[size - 1] = '\0';
+                               break;
+                       }
                }
        }
-#endif
 
-       if (hp->h_addrtype == AF_INET && hp->h_length == 4)
-       {
-               register int i;
+       /*
+       **  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.
+       */
 
-               for (i = 0; hp->h_addr_list[i] != NULL; i++)
+       if (strchr(hostbuf, '.') == NULL &&
+           !getcanonname(hostbuf, size, TRUE))
+       {
+#ifdef LOG
+               syslog(LOG_CRIT, "My unqualified host name (%s) unknown; sleeping for retry",
+                       hostbuf);
+#endif
+               message("My unqualified host name (%s) unknown; sleeping for retry",
+                       hostbuf);
+               sleep(60);
+               if (!getcanonname(hostbuf, size, TRUE))
                {
-                       char ipbuf[100];
-
-                       sprintf(ipbuf, "[%s]",
-                               inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
-                       setclass('w', ipbuf);
+#ifdef LOG
+                       syslog(LOG_ALERT, "unable to qualify my own domain name (%s) -- using short name",
+                               hostbuf);
+#endif
+                       message("WARNING: unable to qualify my own domain name (%s) -- using short name",
+                               hostbuf);
                }
        }
-
-       return (hp->h_aliases);
+       return (hp);
 }
 \f/*
 **  GETAUTHINFO -- get the real host name asociated with a file descriptor
@@ -967,41 +1061,34 @@ myhostname(hostbuf, size)
 **             The user@host information associated with this descriptor.
 */
 
-#if IDENTPROTO
-
 static jmp_buf CtxAuthTimeout;
 
-static
+static void
 authtimeout()
 {
        longjmp(CtxAuthTimeout, 1);
 }
 
-#endif
-
 char *
 getauthinfo(fd)
        int fd;
 {
        int falen;
        register char *p;
-#if IDENTPROTO
        SOCKADDR la;
        int lalen;
        register struct servent *sp;
-       int s;
+       volatile int s;
        int i;
        EVENT *ev;
        int nleft;
        char ibuf[MAXNAME + 1];
-#endif
        static char hbuf[MAXNAME * 2 + 2];
        extern char *hostnamebyanyaddr();
-       extern char RealUserName[];                     /* main.c */
 
        falen = sizeof RealHostAddr;
-       if (getpeername(fd, &RealHostAddr.sa, &falen) < 0 || falen <= 0 ||
-           RealHostAddr.sa.sa_family == 0)
+       if (isatty(fd) || getpeername(fd, &RealHostAddr.sa, &falen) < 0 ||
+           falen <= 0 || RealHostAddr.sa.sa_family == 0)
        {
                (void) sprintf(hbuf, "%s@localhost", RealUserName);
                if (tTd(9, 1))
@@ -1015,7 +1102,6 @@ getauthinfo(fd)
                RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
        }
 
-#if IDENTPROTO
        if (TimeOuts.to_ident == 0)
                goto noident;
 
@@ -1117,6 +1203,14 @@ getauthinfo(fd)
        }
 
        /* p now points to the OSTYPE field */
+       while (isascii(*p) && isspace(*p))
+               p++;
+       if (strncasecmp(p, "other", 5) == 0 &&
+           (p[5] == ':' || p[5] == ' ' || p[5] == ',' || p[5] == '\0'))
+       {
+               /* not useful information */
+               goto noident;
+       }
        p = strchr(p, ':');
        if (p == NULL)
        {
@@ -1133,14 +1227,12 @@ getauthinfo(fd)
        i = strlen(hbuf);
        hbuf[i++] = '@';
        strcpy(&hbuf[i], RealHostName == NULL ? "localhost" : RealHostName);
-       goto finish;
+       goto postident;
 
 closeident:
        (void) close(s);
        clrevent(ev);
 
-#endif /* IDENTPROTO */
-
 noident:
        if (RealHostName == NULL)
        {
@@ -1150,12 +1242,92 @@ noident:
        }
        (void) strcpy(hbuf, RealHostName);
 
-finish:
+postident:
+#if IP_SRCROUTE
+       /*
+       **  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)
+       {
+               int ipoptlen, j;
+               u_char *q;
+               u_char *o;
+               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:
+                               p = &hbuf[strlen(hbuf)];
+                               sprintf(p, " [%s@%.120s",
+                                   *o == IPOPT_SSRR ? "!" : "",
+                                   inet_ntoa(ipopt.ipopt_dst));
+                               p += strlen(p);
+
+                               /* o[1] is option length */
+                               j = *++o / sizeof(struct in_addr) - 1;
+
+                               /* q skips length and router pointer to data */
+                               q = o + 2;
+                               for ( ; j >= 0; j--)
+                               {
+                                       memcpy(&addr, q, sizeof(addr));
+                                       sprintf(p, "%c%.120s",
+                                                    j ? '@' : ':',
+                                                    inet_ntoa(addr));
+                                       p += strlen(p);
+                                       q += sizeof(struct in_addr); 
+                               }
+                               o += *o;
+                               break;
+
+                         default:
+                               /* Skip over option */
+                               o += o[1];
+                               break;
+                       }
+               }
+               strcat(hbuf,"]");
+               goto postipsr;
+       }
+#endif
+
+noipsr:
        if (RealHostName != NULL && RealHostName[0] != '[')
        {
                p = &hbuf[strlen(hbuf)];
-               (void) sprintf(p, " [%s]", anynet_ntoa(&RealHostAddr));
+               (void) sprintf(p, " [%.100s]", anynet_ntoa(&RealHostAddr));
        }
+
+postipsr:
        if (tTd(9, 1))
                printf("getauthinfo: %s\n", hbuf);
        return hbuf;
@@ -1191,13 +1363,8 @@ host_map_lookup(map, name, av, statp)
        register struct hostent *hp;
        struct in_addr in_addr;
        char *cp;
-       int i;
        register STAB *s;
-       char hbuf[MAXNAME];
-       extern struct hostent *gethostbyaddr();
-#if NAMED_BIND
-       extern int h_errno;
-#endif
+       char hbuf[MAXNAME + 1];
 
        /*
        **  See if we have already looked up this name.  If so, just
@@ -1209,21 +1376,38 @@ host_map_lookup(map, name, av, statp)
        {
                if (tTd(9, 1))
                        printf("host_map_lookup(%s) => CACHE %s\n",
-                               name, s->s_namecanon.nc_cname);
+                              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 (CurEnv->e_message == NULL && *statp == EX_TEMPFAIL)
+               if (*statp == EX_TEMPFAIL)
                {
-                       sprintf(hbuf, "%s: Name server timeout",
+                       CurEnv->e_status = "4.4.3";
+                       message("851 %s: Name server timeout",
                                shortenstring(name, 33));
-                       CurEnv->e_message = newstr(hbuf);
                }
                return s->s_namecanon.nc_cname;
        }
 
+       /*
+       **  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
@@ -1238,8 +1422,14 @@ host_map_lookup(map, name, av, statp)
                if (tTd(9, 1))
                        printf("host_map_lookup(%s) => ", name);
                s->s_namecanon.nc_flags |= NCF_VALID;           /* will be soon */
-               (void) strcpy(hbuf, name);
-               if (getcanonname(hbuf, sizeof hbuf - 1, TRUE))
+               if (strlen(name) < sizeof hbuf)
+                       (void) strcpy(hbuf, name);
+               else
+               {
+                       bcopy(name, hbuf, sizeof hbuf - 1);
+                       hbuf[sizeof hbuf - 1] = '\0';
+               }
+               if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX))
                {
                        if (tTd(9, 1))
                                printf("%s\n", hbuf);
@@ -1261,16 +1451,15 @@ host_map_lookup(map, name, av, statp)
                          case TRY_AGAIN:
                                if (UseNameServer)
                                {
-                                       sprintf(hbuf, "%s: Name server timeout",
+                                       CurEnv->e_status = "4.4.3";
+                                       message("851 %s: Name server timeout",
                                                shortenstring(name, 33));
-                                       message("%s", hbuf);
-                                       if (CurEnv->e_message == NULL)
-                                               CurEnv->e_message = newstr(hbuf);
                                }
                                *statp = EX_TEMPFAIL;
                                break;
 
                          case HOST_NOT_FOUND:
+                         case NO_DATA:
                                *statp = EX_NOHOST;
                                break;
 
@@ -1288,33 +1477,15 @@ host_map_lookup(map, name, av, statp)
                        *statp = EX_NOHOST;
 #endif
                        s->s_namecanon.nc_stat = *statp;
-                       if (*statp != EX_TEMPFAIL || UseNameServer)
-                               return NULL;
-
-                       /*
-                       **  Try to look it up in /etc/hosts
-                       */
-
-                       hp = gethostbyname(name);
-                       if (hp == NULL)
-                       {
-                               /* no dice there either */
-                               s->s_namecanon.nc_stat = *statp = EX_NOHOST;
-                               return NULL;
-                       }
-
-                       s->s_namecanon.nc_stat = *statp = EX_OK;
-                       cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av);
-                       s->s_namecanon.nc_cname = newstr(cp);
-                       return cp;
+                       return NULL;
                }
        }
        if ((cp = strchr(name, ']')) == NULL)
                return (NULL);
        *cp = '\0';
        (void) inet_aton(&name[1], &in_addr);
-
-       /* nope -- ask the name server */
+  
+       /* nope -- ask the name server */
        hp = gethostbyaddr((char *)&in_addr, sizeof(in_addr), AF_INET);
        s->s_namecanon.nc_errno = errno;
 #if NAMED_BIND
@@ -1328,7 +1499,7 @@ host_map_lookup(map, name, av, statp)
        }
 
        /* found a match -- copy out */
-       cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av);
+       cp = map_rewrite(map, (char *) hp->h_name, strlen(hp->h_name), av);
        s->s_namecanon.nc_stat = *statp = EX_OK;
        s->s_namecanon.nc_cname = newstr(cp);
        return cp;
@@ -1343,6 +1514,10 @@ host_map_lookup(map, name, av, statp)
 **             A printable version of that sockaddr.
 */
 
+#if NETLINK
+# include <net/if_dl.h>
+#endif
+
 char *
 anynet_ntoa(sap)
        register SOCKADDR *sap;
@@ -1360,8 +1535,7 @@ anynet_ntoa(sap)
 
        switch (sap->sa.sa_family)
        {
-#ifdef MAYBENEXTRELEASE                /*** UNTESTED *** UNTESTED *** UNTESTED ***/
-#ifdef NETUNIX
+#if NETUNIX
          case AF_UNIX:
                if (sap->sunix.sun_path[0] != '\0')
                        sprintf(buf, "[UNIX: %.64s]", sap->sunix.sun_path);
@@ -1369,16 +1543,22 @@ anynet_ntoa(sap)
                        sprintf(buf, "[UNIX: localhost]");
                return buf;
 #endif
-#endif
 
-#ifdef NETINET
+#if NETINET
          case AF_INET:
-               return inet_ntoa(((struct sockaddr_in *) sap)->sin_addr);
+               return inet_ntoa(sap->sin.sin_addr);
 #endif
 
+#if NETLINK
+         case AF_LINK:
+               sprintf(buf, "[LINK: %s]",
+                       link_ntoa((struct sockaddr_dl *) &sap->sa));
+               return buf;
+#endif
          default:
-               /* this case is only to ensure syntactic correctness */
-               break;
+               /* this case is needed when nothing is #defined */
+               /* in order to keep the switch syntactically correct */
+               break;
        }
 
        /* unknown family -- just dump bytes */
@@ -1421,30 +1601,28 @@ hostnamebyanyaddr(sap)
 
        switch (sap->sa.sa_family)
        {
-#ifdef NETINET
+#if NETINET
          case AF_INET:
-               hp = gethostbyaddr((char *) &sap->sin.sin_addr,
-                       sizeof sap->sin.sin_addr,
+               hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr,
+                       INADDRSZ,
                        AF_INET);
                break;
 #endif
 
-#ifdef NETISO
+#if NETISO
          case AF_ISO:
-               hp = gethostbyaddr((char *) &sap->siso.siso_addr,
+               hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr,
                        sizeof sap->siso.siso_addr,
                        AF_ISO);
                break;
 #endif
 
-#ifdef MAYBENEXTRELEASE                /*** UNTESTED *** UNTESTED *** UNTESTED ***/
          case AF_UNIX:
                hp = NULL;
                break;
-#endif
 
          default:
-               hp = gethostbyaddr(sap->sa.sa_data,
+               hp = sm_gethostbyaddr(sap->sa.sa_data,
                           sizeof sap->sa.sa_data,
                           sap->sa.sa_family);
                break;
@@ -1455,13 +1633,13 @@ hostnamebyanyaddr(sap)
 #endif /* NAMED_BIND */
 
        if (hp != NULL)
-               return hp->h_name;
+               return (char *) hp->h_name;
        else
        {
                /* produce a dotted quad */
-               static char buf[512];
+               static char buf[203];
 
-               (void) sprintf(buf, "[%s]", anynet_ntoa(sap));
+               (void) sprintf(buf, "[%.200s]", anynet_ntoa(sap));
                return buf;
        }
 }
@@ -1545,7 +1723,7 @@ host_map_lookup(map, name, avp, statp)
 {
        register struct hostent *hp;
 
-       hp = gethostbyname(name);
+       hp = sm_gethostbyname(name);
        if (hp != NULL)
                return hp->h_name;
        *statp = EX_NOHOST;
index 99e09f6..2435f04 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)deliver.c  8.84.1.4 (Berkeley) 3/28/95";
+static char sccsid[] = "@(#)deliver.c  8.185 (Berkeley) 11/18/95";
 #endif /* not lint */
 
 #include "sendmail.h"
-#include <netdb.h>
 #include <errno.h>
 #if NAMED_BIND
-#include <arpa/nameser.h>
 #include <resolv.h>
 
 extern int     h_errno;
 #endif
 
+#ifdef SMTP
 extern char    SmtpError[];
+#endif
 
 /*
 **  SENDALL -- actually send all the messages.
@@ -66,6 +66,7 @@ extern char   SmtpError[];
 **                     appropriate action.
 */
 
+void
 sendall(e, mode)
        ENVELOPE *e;
        char mode;
@@ -75,7 +76,10 @@ sendall(e, mode)
        int otherowners;
        register ENVELOPE *ee;
        ENVELOPE *splitenv = NULL;
-       bool announcequeueup;
+       bool oldverbose = Verbose;
+       bool somedeliveries = FALSE;
+       int pid;
+       extern void sendenvelope();
 
        /*
        **  If we have had global, fatal errors, don't bother sending
@@ -96,19 +100,20 @@ sendall(e, mode)
        if (mode == SM_DEFAULT)
        {
                mode = e->e_sendmode;
-               if (mode != SM_VERIFY &&
+               if (mode != SM_VERIFY && mode != SM_DEFER &&
                    shouldqueue(e->e_msgpriority, e->e_ctime))
                        mode = SM_QUEUE;
-               announcequeueup = mode == SM_QUEUE;
        }
-       else
-               announcequeueup = FALSE;
 
        if (tTd(13, 1))
        {
+               extern void printenvflags();
+
                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);
        }
@@ -124,12 +129,15 @@ sendall(e, mode)
        if (e->e_hopcount > MaxHopCount)
        {
                errno = 0;
-               queueup(e, TRUE, announcequeueup);
+#ifdef 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",
+               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;
        }
 
@@ -151,7 +159,7 @@ sendall(e, mode)
                        printaddr(&e->e_from, FALSE);
                }
                e->e_from.q_flags |= QDONTSEND;
-               (void) recipient(&e->e_from, &e->e_sendqueue, e);
+               (void) recipient(&e->e_from, &e->e_sendqueue, 0, e);
        }
 
        /*
@@ -185,8 +193,17 @@ sendall(e, mode)
 
                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 (q->q_owner != NULL)
                        {
@@ -210,6 +227,35 @@ sendall(e, mode)
                        {
                                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;
+                       }
+                       else
+                       {
+                               if (tTd(13, 30))
+                                       printf("    ... deliverable\n");
+                               somedeliveries = TRUE;
+                       }
                }
 
                if (owner != NULL && otherowners > 0)
@@ -233,7 +279,7 @@ sendall(e, mode)
                        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);
+                       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, TRUE);
                        if (tTd(13, 5))
@@ -244,33 +290,61 @@ sendall(e, mode)
                        ee->e_from.q_flags |= QDONTSEND;
                        ee->e_dfp = NULL;
                        ee->e_xfp = NULL;
-                       ee->e_df = 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;
                                }
+                       }
                        for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
+                       {
                                if (q->q_owner != owner)
                                {
                                        q->q_flags |= QDONTSEND;
                                        q->q_flags &= ~QQUEUEUP;
                                }
+                               else
+                               {
+                                       /* clear DSN parameters */
+                                       q->q_flags &= ~(QHASNOTIFY|QPINGONSUCCESS);
+                                       q->q_flags |= QPINGONFAILURE|QPINGONDELAY;
+                               }
+                       }
 
-                       if (e->e_df != NULL && mode != SM_VERIFY)
+                       if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags))
                        {
+                               char df1buf[20], df2buf[20];
+
                                ee->e_dfp = NULL;
-                               ee->e_df = queuename(ee, 'd');
-                               ee->e_df = newstr(ee->e_df);
-                               if (link(e->e_df, ee->e_df) < 0)
+                               strcpy(df1buf, queuename(e, 'd'));
+                               strcpy(df2buf, queuename(ee, 'd'));
+                               if (link(df1buf, df2buf) < 0)
                                {
+                                       int saverrno = errno;
+
                                        syserr("sendall: link(%s, %s)",
-                                               e->e_df, ee->e_df);
+                                               df1buf, df2buf);
+                                       if (saverrno == EEXIST)
+                                       {
+                                               if (unlink(df2buf) < 0)
+                                               {
+                                                       syserr("!sendall: unlink(%s): permanent",
+                                                               df2buf);
+                                                       /*NOTREACHED*/
+                                               }
+                                               if (link(df1buf, df2buf) < 0)
+                                               {
+                                                       syserr("!sendall: link(%s, %s): permanent",
+                                                               df1buf, df2buf);
+                                                       /*NOTREACHED*/
+                                               }
+                                       }
                                }
                        }
 #ifdef LOG
@@ -294,65 +368,33 @@ sendall(e, mode)
                e->e_flags |= EF_NORECEIPT;
        }
 
+       /* 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;
+       }
+
 # ifdef QUEUE
-       if ((mode == SM_QUEUE || mode == SM_FORK ||
+       if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK ||
             (mode != SM_VERIFY && SuperSafe)) &&
            !bitset(EF_INQUEUE, e->e_flags))
        {
                /* be sure everything is instantiated in the queue */
-               queueup(e, TRUE, announcequeueup);
+               queueup(e, mode == SM_QUEUE || mode == SM_DEFER);
                for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
-                       queueup(ee, TRUE, announcequeueup);
+                       queueup(ee, mode == SM_QUEUE || mode == SM_DEFER);
        }
 #endif /* QUEUE */
 
-       if (splitenv != NULL)
-       {
-               if (tTd(13, 1))
-               {
-                       printf("\nsendall: Split queue; remaining queue:\n");
-                       printaddr(e->e_sendqueue, TRUE);
-               }
-
-               for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
-               {
-                       CurEnv = ee;
-                       if (mode != SM_VERIFY)
-                               openxscript(ee);
-                       sendenvelope(ee, mode);
-                       dropenvelope(ee);
-               }
-
-               CurEnv = e;
-       }
-       sendenvelope(e, mode);
-}
-
-sendenvelope(e, mode)
-       register ENVELOPE *e;
-       char mode;
-{
-       bool oldverbose;
-       int pid;
-       register ADDRESS *q;
-       char *qf;
-       char *id;
-
        /*
-       **  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 we belong in background, fork now.
        */
 
-       if (bitset(EF_FATALERRS, e->e_flags) &&
-           (OpMode == MD_SMTP || OpMode == MD_DAEMON))
-       {
-               e->e_flags |= EF_CLRQUEUE;
-               return;
-       }
-
-       oldverbose = Verbose;
+       if (tTd(13, 20))
+               printf("sendall: final mode = %c\n", mode);
        switch (mode)
        {
          case SM_VERIFY:
@@ -360,8 +402,10 @@ sendenvelope(e, mode)
                break;
 
          case SM_QUEUE:
+         case SM_DEFER:
   queueonly:
-               e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
+               if (e->e_nrcpts > 0)
+                       e->e_flags |= EF_INQUEUE;
                return;
 
          case SM_FORK:
@@ -376,15 +420,17 @@ sendenvelope(e, mode)
                **  then restart from scratch in the child.
                */
 
-               /* save id for future use */
-               id = e->e_id;
+               {
+                       /* save id for future use */
+                       char *qid = e->e_id;
 
-               /* now drop the envelope in the parent */
-               e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
-               dropenvelope(e);
+                       /* now drop the envelope in the parent */
+                       e->e_flags |= EF_INQUEUE;
+                       dropenvelope(e);
 
-               /* and reacquire in the child */
-               (void) dowork(id, TRUE, FALSE, e);
+                       /* and reacquire in the child */
+                       (void) dowork(qid, TRUE, FALSE, e);
+               }
 
                return;
 
@@ -400,15 +446,16 @@ sendenvelope(e, mode)
                        /* be sure we leave the temp files to our child */
                        /* can't call unlockqueue to avoid unlink of xfp */
                        if (e->e_lockfp != NULL)
-                               (void) xfclose(e->e_lockfp, "sendenvelope", "lockfp");
+                               (void) xfclose(e->e_lockfp, "sendenvelope lockfp", e->e_id);
                        e->e_lockfp = NULL;
 
                        /* close any random open files in the envelope */
                        closexscript(e);
                        if (e->e_dfp != NULL)
-                               (void) xfclose(e->e_dfp, "sendenvelope", e->e_df);
+                               (void) xfclose(e->e_dfp, "sendenvelope dfp", e->e_id);
                        e->e_dfp = NULL;
-                       e->e_id = e->e_df = NULL;
+                       e->e_id = NULL;
+                       e->e_flags &= ~EF_HAS_DF;
 
                        /* catch intermediate zombie */
                        (void) waitfor(pid);
@@ -426,7 +473,7 @@ sendenvelope(e, mode)
                /* prevent parent from waiting if there was an error */
                if (pid < 0)
                {
-                       e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
+                       e->e_flags |= EF_INQUEUE;
                        finis();
                }
 
@@ -447,6 +494,64 @@ sendenvelope(e, mode)
                break;
        }
 
+       if (splitenv != NULL)
+       {
+               if (tTd(13, 2))
+               {
+                       printf("\nsendall: Split queue; remaining queue:\n");
+                       printaddr(e->e_sendqueue, TRUE);
+               }
+
+               for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
+               {
+                       CurEnv = ee;
+                       if (mode != SM_VERIFY)
+                               openxscript(ee);
+                       sendenvelope(ee, mode);
+                       dropenvelope(ee);
+               }
+
+               CurEnv = e;
+       }
+       sendenvelope(e, mode);
+       Verbose = oldverbose;
+       if (mode == SM_FORK)
+               finis();
+}
+
+void
+sendenvelope(e, mode)
+       register ENVELOPE *e;
+       char mode;
+{
+       register ADDRESS *q;
+       bool didany;
+
+       if (tTd(13, 10))
+               printf("sendenvelope(%s) e_flags=0x%x\n",
+                       e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
+                       e->e_flags);
+#ifdef LOG
+       if (LogLevel > 80)
+               syslog(LOG_DEBUG, "%s: sendenvelope, flags=0x%x",
+                       e->e_id == NULL ? "[NOQUEUE]" : e->e_id,
+                       e->e_flags);
+#endif
+
+       /*
+       **  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.
        **
@@ -456,14 +561,15 @@ sendenvelope(e, mode)
 
        e->e_nsent = 0;
        e->e_flags |= EF_GLOBALERRS;
+       didany = FALSE;
 
        /* now run through the queue */
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
-#ifdef XDEBUG
+#if XDEBUG
                char wbuf[MAXNAME + 20];
 
-               (void) sprintf(wbuf, "sendall(%s)", q->q_paddr);
+               (void) sprintf(wbuf, "sendall(%.*s)", MAXNAME, q->q_paddr);
                checkfd012(wbuf);
 #endif
                if (mode == SM_VERIFY)
@@ -491,21 +597,23 @@ sendenvelope(e, mode)
 
                        if (e->e_nsent >= CheckpointInterval)
                        {
-                               queueup(e, TRUE, FALSE);
+                               queueup(e, FALSE);
                                e->e_nsent = 0;
                        }
 # endif /* QUEUE */
                        (void) deliver(e, q);
+                       didany = TRUE;
                }
        }
-       Verbose = oldverbose;
+       if (didany)
+       {
+               e->e_dtime = curtime();
+               e->e_ntries++;
+       }
 
-#ifdef XDEBUG
+#if XDEBUG
        checkfd012("end of sendenvelope");
 #endif
-
-       if (mode == SM_FORK)
-               finis();
 }
 \f/*
 **  DOFORK -- do a fork, retrying a couple of times on failure.
@@ -563,9 +671,10 @@ sendenvelope(e, mode)
 **             returns twice, once in parent and once in child.
 */
 
+int
 dofork()
 {
-       register int pid;
+       register int pid = -1;
 
        DOFORK(fork);
        return (pid);
@@ -592,6 +701,7 @@ dofork()
 **             The standard input is passed off to someone.
 */
 
+int
 deliver(e, firstto)
        register ENVELOPE *e;
        ADDRESS *firstto;
@@ -602,22 +712,24 @@ deliver(e, firstto)
        register char **mvp;
        register char *p;
        register MAILER *m;             /* mailer for this recipient */
-       ADDRESS *ctladdr;
-       register MCI *mci;
+       ADDRESS *volatile ctladdr;
+       register MCI *volatile mci;
        register ADDRESS *to = firstto;
-       bool clever = FALSE;            /* running user smtp to this mailer */
-       ADDRESS *tochain = NULL;        /* chain of users in this mailer call */
+       volatile bool clever = FALSE;   /* running user smtp to this mailer */
+       ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */
        int rcode;                      /* response code */
        char *firstsig;                 /* signature of firstto */
-       int pid;
-       char *curhost;
+       int pid = -1;
+       char *volatile curhost;
+       time_t xstart;
        int mpvect[2];
        int rpvect[2];
        char *pv[MAXPV+1];
        char tobuf[TOBUFSIZE];          /* text line of to people */
-       char buf[MAXNAME];
-       char rpathbuf[MAXNAME];         /* translated return path */
+       char buf[MAXNAME + 1];
+       char rpathbuf[MAXNAME + 1];     /* translated return path */
        extern int checkcompat();
+       extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int));
 
        errno = 0;
        if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags))
@@ -636,7 +748,10 @@ deliver(e, firstto)
        host = to->q_host;
        CurEnv = e;                     /* just in case */
        e->e_statmsg = NULL;
+#ifdef SMTP
        SmtpError[0] = '\0';
+#endif
+       xstart = curtime();
 
        if (tTd(10, 1))
                printf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n",
@@ -644,33 +759,6 @@ deliver(e, firstto)
        if (tTd(10, 100))
                printopenfds(FALSE);
 
-       /*
-       **  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.
-       **              This should be on a per-mailer basis.
-       */
-
-       if (NoConnect && bitnset(M_EXPENSIVE, m->m_flags) && !Verbose)
-       {
-               for (; to != NULL; to = to->q_next)
-               {
-                       if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags) ||
-                           to->q_mailer != m)
-                               continue;
-                       to->q_flags |= QQUEUEUP;
-                       e->e_to = to->q_paddr;
-                       message("queued");
-                       if (LogLevel > 8)
-                               logdelivery(m, NULL, "queued", NULL, e);
-               }
-               e->e_to = NULL;
-               return (0);
-       }
-
        /*
        **  Do initial argv setup.
        **      Insert the mailer name.  Notice that $x expansion is
@@ -684,7 +772,11 @@ deliver(e, firstto)
 
        /* rewrite from address, using rewriting rules */
        rcode = EX_OK;
-       (void) strcpy(rpathbuf, remotename(e->e_from.q_paddr, m,
+       if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
+               p = e->e_sender;
+       else
+               p = e->e_from.q_paddr;
+       (void) strcpy(rpathbuf, remotename(p, m,
                                           RF_SENDERADDR|RF_CANONICAL,
                                           &rcode, e));
        define('g', rpathbuf, e);               /* translated return path */
@@ -726,7 +818,7 @@ deliver(e, firstto)
                        break;
 
                /* this entry is safe -- go ahead and process it */
-               expand(*mvp, buf, &buf[sizeof buf - 1], e);
+               expand(*mvp, buf, sizeof buf, e);
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV - 3])
                {
@@ -788,11 +880,15 @@ deliver(e, firstto)
                }
 
                /* compute effective uid/gid when sending */
-               /* XXX perhaps this should be to->q_mailer != LocalMailer ?? */
-               /* XXX perhaps it should be a mailer flag? */
-               if (to->q_mailer == ProgMailer || to->q_mailer == FileMailer)
+               if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags))
                        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))
@@ -809,16 +905,20 @@ deliver(e, firstto)
 
                if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
                {
-                       NoReturn = TRUE;
+                       e->e_flags |= EF_NO_BODY_RETN;
+                       to->q_status = "5.2.3";
                        usrerr("552 Message is too large; %ld bytes max", m->m_maxsize);
-                       giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, e);
+                       giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, xstart, e);
                        continue;
                }
+#if NAMED_BIND
+               h_errno = 0;
+#endif
                rcode = checkcompat(to, e);
                if (rcode != EX_OK)
                {
-                       markfailure(e, to, rcode);
-                       giveresponse(rcode, m, NULL, ctladdr, e);
+                       markfailure(e, to, NULL, rcode);
+                       giveresponse(rcode, m, NULL, ctladdr, xstart, e);
                        continue;
                }
 
@@ -860,12 +960,25 @@ deliver(e, firstto)
                **      with the others, so we fudge on the To person.
                */
 
-               if (m == FileMailer)
+               if (strcmp(m->m_mailer, "[FILE]") == 0)
                {
-                       rcode = mailfile(user, ctladdr, e);
-                       giveresponse(rcode, m, NULL, ctladdr, e);
+                       rcode = mailfile(user, ctladdr, SFF_CREAT, e);
+                       giveresponse(rcode, m, NULL, ctladdr, xstart, e);
+                       e->e_nsent++;
                        if (rcode == EX_OK)
+                       {
                                to->q_flags |= QSENT;
+                               if (bitnset(M_LOCALMAILER, m->m_flags) &&
+                                   (e->e_receiptto != NULL ||
+                                    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();
                        continue;
                }
 
@@ -893,7 +1006,7 @@ deliver(e, firstto)
 
                if (!clever)
                {
-                       expand(*mvp, buf, &buf[sizeof buf - 1], e);
+                       expand(*mvp, buf, sizeof buf, e);
                        *pvp++ = newstr(buf);
                        if (pvp >= &pv[MAXPV - 2])
                        {
@@ -919,7 +1032,7 @@ deliver(e, firstto)
 
        while (!clever && *++mvp != NULL)
        {
-               expand(*mvp, buf, &buf[sizeof buf - 1], e);
+               expand(*mvp, buf, sizeof buf, e);
                *pvp++ = newstr(buf);
                if (pvp >= &pv[MAXPV])
                        syserr("554 deliver: pv overflow after $u for %s", pv[0]);
@@ -935,7 +1048,7 @@ deliver(e, firstto)
        */
 
        /*XXX this seems a bit wierd */
-       if (ctladdr == NULL && m != ProgMailer &&
+       if (ctladdr == NULL && m != ProgMailer && m != FileMailer &&
            bitset(QGOODUID, e->e_from.q_flags))
                ctladdr = &e->e_from;
 
@@ -950,8 +1063,11 @@ deliver(e, firstto)
                printav(pv);
        }
        errno = 0;
+#if NAMED_BIND
+       h_errno = 0;
+#endif
 
-       CurHostName = m->m_mailer;
+       CurHostName = NULL;
 
        /*
        **  Deal with the special case of mail handled through an IPC
@@ -966,16 +1082,31 @@ deliver(e, firstto)
        SmtpPhase = NULL;
        mci = NULL;
 
-#ifdef XDEBUG
+#if XDEBUG
        {
                char wbuf[MAXLINE];
 
                /* make absolutely certain 0, 1, and 2 are in use */
-               sprintf(wbuf, "%s... openmailer(%s)", e->e_to, m->m_name);
+               sprintf(wbuf, "%s... openmailer(%s)",
+                       shortenstring(e->e_to, 203), 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;
+       }
+
        /* check for Local Person Communication -- not for mortals!!! */
        if (strcmp(m->m_mailer, "[LPC]") == 0)
        {
@@ -991,7 +1122,7 @@ deliver(e, firstto)
        {
 #ifdef DAEMON
                register int i;
-               register u_short port;
+               register volatile u_short port = 0;
 
                if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0')
                {
@@ -1017,14 +1148,23 @@ deliver(e, firstto)
                        goto give_up;
                }
                if (pv[2] != NULL)
-                       port = atoi(pv[2]);
-               else
-                       port = 0;
+               {
+                       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')
                {
                        register char *p;
-                       static char hostbuf[MAXNAME];
+                       static char hostbuf[MAXNAME + 1];
 
                        /* pull the next host from the signature */
                        p = strchr(curhost, ':');
@@ -1053,6 +1193,9 @@ tryhost:
                                        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;
@@ -1061,10 +1204,11 @@ tryhost:
 
                        /* try the connection */
                        setproctitle("%s %s: %s", e->e_id, hostbuf, "user open");
-                       message("Connecting to %s (%s)...",
+                       message("Connecting to %s via %s...",
                                hostbuf, m->m_name);
                        i = makeconnection(hostbuf, port, mci,
                                bitnset(M_SECURE_PORT, m->m_flags));
+                       mci->mci_lastuse = curtime();
                        mci->mci_exitstat = i;
                        mci->mci_errno = errno;
 #if NAMED_BIND
@@ -1105,6 +1249,14 @@ tryhost:
        }
        else
        {
+               /* flush any expired connections */
+               (void) mci_scan(NULL);
+
+               /* 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;
@@ -1119,7 +1271,7 @@ tryhost:
                if (pipe(mpvect) < 0)
                {
                        syserr("%s... openmailer(%s): pipe (to mailer)",
-                               e->e_to, m->m_name);
+                               shortenstring(e->e_to, 203), m->m_name);
                        if (tTd(11, 1))
                                printf("openmailer: NULL\n");
                        rcode = EX_OSERR;
@@ -1127,10 +1279,11 @@ tryhost:
                }
 
                /* if this mailer speaks smtp, create a return pipe */
+#ifdef SMTP
                if (clever && pipe(rpvect) < 0)
                {
                        syserr("%s... openmailer(%s): pipe (from mailer)",
-                               e->e_to, m->m_name);
+                               shortenstring(e->e_to, 203), m->m_name);
                        (void) close(mpvect[0]);
                        (void) close(mpvect[1]);
                        if (tTd(11, 1))
@@ -1138,6 +1291,7 @@ tryhost:
                        rcode = EX_OSERR;
                        goto give_up;
                }
+#endif
 
                /*
                **  Actually fork the mailer process.
@@ -1159,14 +1313,16 @@ tryhost:
                {
                        /* failure */
                        syserr("%s... openmailer(%s): cannot fork",
-                               e->e_to, m->m_name);
+                               shortenstring(e->e_to, 203), m->m_name);
                        (void) close(mpvect[0]);
                        (void) close(mpvect[1]);
+#ifdef SMTP
                        if (clever)
                        {
                                (void) close(rpvect[0]);
                                (void) close(rpvect[1]);
                        }
+#endif
                        if (tTd(11, 1))
                                printf("openmailer: NULL\n");
                        rcode = EX_OSERR;
@@ -1176,9 +1332,7 @@ tryhost:
                {
                        int i;
                        int saveerrno;
-                       char **ep;
-                       char *env[MAXUSERENVIRON];
-                       extern char **environ;
+                       struct stat stb;
                        extern int DtableSize;
 
                        if (e->e_lockfp != NULL)
@@ -1189,23 +1343,61 @@ tryhost:
                        (void) setsignal(SIGHUP, SIG_IGN);
                        (void) setsignal(SIGTERM, SIG_DFL);
 
-                       /* reset user and group */
-                       if (!bitnset(M_RESTR, m->m_flags))
+                       if (m != FileMailer || stat(tochain->q_user, &stb) < 0)
+                               stb.st_mode = 0;
+
+                       /* tweak niceness */
+                       if (m->m_nice != 0)
+                               nice(m->m_nice);
+
+                       /* reset group id */
+                       if (bitnset(M_SPECIFIC_UID, m->m_flags))
+                               (void) setgid(m->m_gid);
+                       else if (bitset(S_ISGID, stb.st_mode))
+                               (void) setgid(stb.st_gid);
+                       else if (ctladdr != NULL && ctladdr->q_gid != 0)
                        {
-                               if (ctladdr == NULL || ctladdr->q_uid == 0)
-                               {
+                               if (!DontInitGroups)
+                                       (void) initgroups(ctladdr->q_ruser != NULL ?
+                                               ctladdr->q_ruser : ctladdr->q_user,
+                                               ctladdr->q_gid);
+                               (void) setgid(ctladdr->q_gid);
+                       }
+                       else
+                       {
+                               if (!DontInitGroups)
                                        (void) initgroups(DefUser, DefGid);
+                               if (m->m_gid == 0)
                                        (void) setgid(DefGid);
+                               else
+                                       (void) setgid(m->m_gid);
+                       }
+
+                       /* reset user id */
+                       endpwent();
+                       if (bitnset(M_SPECIFIC_UID, m->m_flags))
+                       {
+#if USESETEUID
+                               (void) seteuid(m->m_uid);
+#else
+# if HASSETREUID
+                               (void) setreuid(-1, m->m_uid);
+# else
+                               if (m->m_uid != geteuid())
+                                       (void) setuid(m->m_uid);
+# endif
+#endif
+                       }
+                       else if (bitset(S_ISUID, stb.st_mode))
+                               (void) setuid(stb.st_uid);
+                       else if (ctladdr != NULL && ctladdr->q_uid != 0)
+                               (void) setuid(ctladdr->q_uid);
+                       else
+                       {
+                               if (m->m_uid == 0)
                                        (void) setuid(DefUid);
-                               }
                                else
-                               {
-                                       (void) initgroups(ctladdr->q_ruser?
-                                               ctladdr->q_ruser: ctladdr->q_user,
-                                               ctladdr->q_gid);
-                                       (void) setgid(ctladdr->q_gid);
-                                       (void) setuid(ctladdr->q_uid);
-                               }
+                                       (void) setuid(m->m_uid);
                        }
 
                        if (tTd(11, 2))
@@ -1216,14 +1408,14 @@ tryhost:
                        if (m->m_execdir != NULL)
                        {
                                char *p, *q;
-                               char buf[MAXLINE];
+                               char buf[MAXLINE + 1];
 
                                for (p = m->m_execdir; p != NULL; p = q)
                                {
                                        q = strchr(p, ':');
                                        if (q != NULL)
                                                *q = '\0';
-                                       expand(p, buf, &buf[sizeof buf] - 1, e);
+                                       expand(p, buf, sizeof buf, e);
                                        if (q != NULL)
                                                *q++ = ':';
                                        if (tTd(11, 20))
@@ -1235,13 +1427,15 @@ tryhost:
                        }
 
                        /* arrange to filter std & diag output of command */
+#ifdef SMTP
                        if (clever)
                        {
                                (void) close(rpvect[0]);
                                if (dup2(rpvect[1], STDOUT_FILENO) < 0)
                                {
                                        syserr("%s... openmailer(%s): cannot dup pipe %d for stdout",
-                                               e->e_to, m->m_name, rpvect[1]);
+                                               shortenstring(e->e_to, 203),
+                                               m->m_name, rpvect[1]);
                                        _exit(EX_OSERR);
                                }
                                (void) close(rpvect[1]);
@@ -1253,15 +1447,16 @@ tryhost:
                                if (dup2(fileno(e->e_xfp), STDOUT_FILENO) < 0)
                                {
                                        syserr("%s... openmailer(%s): cannot dup xscript %d for stdout",
-                                               e->e_to, m->m_name,
-                                               fileno(e->e_xfp));
+                                               shortenstring(e->e_to, 203),
+                                               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",
-                                       e->e_to, m->m_name);
+                                       shortenstring(e->e_to, 203), m->m_name);
                                _exit(EX_OSERR);
                        }
 
@@ -1270,7 +1465,8 @@ tryhost:
                        if (dup2(mpvect[0], STDIN_FILENO) < 0)
                        {
                                syserr("%s... openmailer(%s): cannot dup pipe %d for stdin",
-                                       e->e_to, m->m_name, mpvect[0]);
+                                       shortenstring(e->e_to, 203),
+                                       m->m_name, mpvect[0]);
                                _exit(EX_OSERR);
                        }
                        (void) close(mpvect[0]);
@@ -1284,32 +1480,15 @@ tryhost:
                                        (void) fcntl(i, F_SETFD, j | 1);
                        }
 
-                       /*
-                       **  Set up the mailer environment
-                       **      TZ is timezone information.
-                       **      SYSTYPE is Apollo software sys type (required).
-                       **      ISP is Apollo hardware system type (required).
-                       */
-
-                       i = 0;
-                       env[i++] = "AGENT=sendmail";
-                       for (ep = environ; *ep != NULL; ep++)
-                       {
-                               if (strncmp(*ep, "TZ=", 3) == 0 ||
-                                   strncmp(*ep, "ISP=", 4) == 0 ||
-                                   strncmp(*ep, "SYSTYPE=", 8) == 0)
-                                       env[i++] = *ep;
-                       }
-                       env[i++] = NULL;
-
                        /* run disconnected from terminal */
                        (void) setsid();
 
                        /* try to execute the mailer */
-                       execve(m->m_mailer, pv, env);
+                       execve(m->m_mailer, (ARGV_T) pv, (ARGV_T) UserEnviron);
                        saveerrno = errno;
                        syserr("Cannot exec %s", m->m_mailer);
-                       if (m == LocalMailer || transienterror(saveerrno))
+                       if (bitnset(M_LOCALMAILER, m->m_flags) ||
+                           transienterror(saveerrno))
                                _exit(EX_OSERR);
                        _exit(EX_UNAVAILABLE);
                }
@@ -1330,14 +1509,17 @@ tryhost:
                        syserr("deliver: cannot create mailer output channel, fd=%d",
                                mpvect[1]);
                        (void) close(mpvect[1]);
+#ifdef SMTP
                        if (clever)
                        {
                                (void) close(rpvect[0]);
                                (void) close(rpvect[1]);
                        }
+#endif
                        rcode = EX_OSERR;
                        goto give_up;
                }
+#ifdef SMTP
                if (clever)
                {
                        (void) close(rpvect[1]);
@@ -1354,6 +1536,7 @@ tryhost:
                        }
                }
                else
+#endif
                {
                        mci->mci_flags |= MCIF_TEMP;
                        mci->mci_in = NULL;
@@ -1364,10 +1547,21 @@ tryhost:
        **  If we are in SMTP opening state, send initial protocol.
        */
 
+       if (bitnset(M_7BITS, m->m_flags) &&
+           (!clever || mci->mci_state == MCIS_CLOSED))
+               mci->mci_flags |= MCIF_7BIT;
+#ifdef SMTP
        if (clever && mci->mci_state != MCIS_CLOSED)
-       {
                smtpinit(m, mci, e);
-       }
+#endif
+
+       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;
+       else
+               mci->mci_flags &= ~MCIF_CVT8TO7;
+
        if (tTd(11, 1))
        {
                printf("openmailer: ");
@@ -1389,11 +1583,13 @@ tryhost:
                                rcode, mci->mci_state, firstsig);
                        rcode = EX_SOFTWARE;
                }
-               else if (rcode == EX_TEMPFAIL && curhost != NULL && *curhost != '\0')
+#ifdef DAEMON
+               else if (curhost != NULL && *curhost != '\0')
                {
                        /* try next MX site */
                        goto tryhost;
                }
+#endif
        }
        else if (!clever)
        {
@@ -1402,8 +1598,7 @@ tryhost:
                */
 
                putfromline(mci, e);
-               (*e->e_puthdr)(mci, e);
-               putline("\n", mci);
+               (*e->e_puthdr)(mci, e->e_header, e);
                (*e->e_putbody)(mci, e, NULL);
 
                /* get the exit status */
@@ -1429,8 +1624,8 @@ tryhost:
                                e->e_to = to->q_paddr;
                                if ((i = smtprcpt(to, m, mci, e)) != EX_OK)
                                {
-                                       markfailure(e, to, i);
-                                       giveresponse(i, m, mci, ctladdr, e);
+                                       markfailure(e, to, mci, i);
+                                       giveresponse(i, m, mci, ctladdr, xstart, e);
                                }
                                else
                                {
@@ -1494,21 +1689,40 @@ tryhost:
 
   give_up:
        if (tobuf[0] != '\0')
-               giveresponse(rcode, m, mci, ctladdr, e);
+               giveresponse(rcode, m, mci, ctladdr, xstart, e);
        for (to = tochain; to != NULL; to = to->q_tchain)
        {
+               /* see if address already marked */
+               if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
+                       continue;
+
+               /* mark bad addresses */
                if (rcode != EX_OK)
-                       markfailure(e, to, rcode);
-               else
                {
-                       to->q_flags |= QSENT;
-                       e->e_nsent++;
-                       if (e->e_receiptto != NULL &&
-                           bitnset(M_LOCALMAILER, m->m_flags))
-                       {
-                               fprintf(e->e_xfp, "%s... Successfully delivered\n",
-                                       to->q_paddr);
-                       }
+                       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) &&
+                   (e->e_receiptto != NULL ||
+                    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);
                }
        }
 
@@ -1516,13 +1730,14 @@ tryhost:
        **  Restore state and return.
        */
 
-#ifdef XDEBUG
+#if XDEBUG
        {
                char wbuf[MAXLINE];
 
                /* make absolutely certain 0, 1, and 2 are in use */
                sprintf(wbuf, "%s... end of deliver(%s)",
-                       e->e_to == NULL ? "NO-TO-LIST" : e->e_to,
+                       e->e_to == NULL ? "NO-TO-LIST"
+                                       : shortenstring(e->e_to, 203),
                        m->m_name);
                checkfd012(wbuf);
        }
@@ -1538,6 +1753,7 @@ tryhost:
 **     Parameters:
 **             e -- the envelope we are sending.
 **             q -- the address to mark.
+**             mci -- mailer connection information.
 **             rcode -- the code signifying the particular failure.
 **
 **     Returns:
@@ -1549,12 +1765,14 @@ tryhost:
 **                     the message will be queued, as appropriate.
 */
 
-markfailure(e, q, rcode)
+void
+markfailure(e, q, mci, rcode)
        register ENVELOPE *e;
        register ADDRESS *q;
+       register MCI *mci;
        int rcode;
 {
-       char buf[MAXLINE];
+       char *stat = NULL;
 
        switch (rcode)
        {
@@ -1571,6 +1789,71 @@ markfailure(e, q, rcode)
                q->q_flags |= QBADADDR;
                break;
        }
+
+       /* find most specific error code possible */
+       if (q->q_status == NULL && mci != NULL)
+               q->q_status = mci->mci_status;
+       if (q->q_status == NULL)
+               q->q_status = e->e_status;
+       if (q->q_status == NULL)
+       {
+               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) sprintf(buf, "%d", rcode);
+               q->q_rstatus = newstr(buf);
+       }
 }
 \f/*
 **  ENDMAILER -- Wait for mailer to terminate.
@@ -1593,6 +1876,7 @@ markfailure(e, q, rcode)
 **             none.
 */
 
+int
 endmailer(mci, e, pv)
        register MCI *mci;
        register ENVELOPE *e;
@@ -1656,6 +1940,8 @@ endmailer(mci, e, pv)
 **                     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:
@@ -1666,11 +1952,13 @@ endmailer(mci, e, pv)
 **             ExitStat may be set.
 */
 
-giveresponse(stat, m, mci, ctladdr, e)
+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;
@@ -1689,7 +1977,8 @@ giveresponse(stat, m, mci, ctladdr, e)
                statmsg = "250 Sent";
                if (e->e_statmsg != NULL)
                {
-                       (void) sprintf(buf, "%s (%s)", statmsg, e->e_statmsg);
+                       (void) sprintf(buf, "%s (%s)",
+                               statmsg, shortenstring(e->e_statmsg, 403));
                        statmsg = buf;
                }
        }
@@ -1773,7 +2062,7 @@ giveresponse(stat, m, mci, ctladdr, e)
        */
 
        if (LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6))
-               logdelivery(m, mci, &statmsg[4], ctladdr, e);
+               logdelivery(m, mci, &statmsg[4], ctladdr, xstart, e);
 
        if (tTd(11, 2))
                printf("giveresponse: stat=%d, e->e_message=%s\n",
@@ -1805,6 +2094,8 @@ giveresponse(stat, m, mci, ctladdr, e)
 **                     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:
@@ -1814,20 +2105,25 @@ giveresponse(stat, m, mci, ctladdr, e)
 **             none
 */
 
-logdelivery(m, mci, stat, ctladdr, e)
+#define SPACELEFT(bp)  (sizeof buf - ((bp) - buf))
+
+void
+logdelivery(m, mci, stat, ctladdr, xstart, e)
        MAILER *m;
        register MCI *mci;
-       char *stat;
+       const char *stat;
        ADDRESS *ctladdr;
+       time_t xstart;
        register ENVELOPE *e;
 {
 # ifdef LOG
        register char *bp;
        register char *p;
        int l;
-       char buf[512];
+       char buf[1024];
 
 #  if (SYSLOG_BUFSIZE) >= 256
+       /* ctladdr: max 106 bytes */
        bp = buf;
        if (ctladdr != NULL)
        {
@@ -1836,35 +2132,48 @@ logdelivery(m, mci, stat, ctladdr, e)
                bp += strlen(bp);
                if (bitset(QGOODUID, ctladdr->q_flags))
                {
-                       (void) sprintf(bp, " (%d/%d)",
+                       (void) snprintf(bp, SPACELEFT(bp), " (%d/%d)",
                                        ctladdr->q_uid, ctladdr->q_gid);
                        bp += strlen(bp);
                }
        }
 
-       (void) sprintf(bp, ", delay=%s", pintvl(curtime() - e->e_ctime, TRUE));
+       /* delay & xdelay: max 41 bytes */
+       snprintf(bp, SPACELEFT(bp), ", delay=%s",
+               pintvl(curtime() - e->e_ctime, TRUE));
        bp += strlen(bp);
 
+       if (xstart != (time_t) 0)
+       {
+               snprintf(bp, SPACELEFT(bp), ", xdelay=%s",
+                       pintvl(curtime() - xstart, TRUE));
+               bp += strlen(bp);
+       }
+
+       /* mailer: assume about 19 bytes (max 10 byte mailer name) */
        if (m != NULL)
        {
-               (void) strcpy(bp, ", mailer=");
-               (void) strcat(bp, m->m_name);
+               snprintf(bp, SPACELEFT(bp), ", mailer=%s", m->m_name);
                bp += strlen(bp);
        }
 
+       /* relay: max 66 bytes for IPv4 addresses */
        if (mci != NULL && mci->mci_host != NULL)
        {
 # ifdef DAEMON
                extern SOCKADDR CurHostAddr;
 # endif
 
-               (void) strcpy(bp, ", relay=");
-               (void) strcat(bp, mci->mci_host);
+               snprintf(bp, SPACELEFT(bp), ", relay=%s",
+                       shortenstring(mci->mci_host, 40));
+               bp += strlen(bp);
 
 # ifdef DAEMON
-               (void) strcat(bp, " [");
-               (void) strcat(bp, anynet_ntoa(&CurHostAddr));
-               (void) strcat(bp, "]");
+               if (CurHostAddr.sa.sa_family != 0)
+               {
+                       snprintf(bp, SPACELEFT(bp), " [%s]",
+                               anynet_ntoa(&CurHostAddr));
+               }
 # endif
        }
        else if (strcmp(stat, "queued") != 0)
@@ -1873,8 +2182,8 @@ logdelivery(m, mci, stat, ctladdr, e)
 
                if (p != NULL && p[0] != '\0')
                {
-                       (void) strcpy(bp, ", relay=");
-                       (void) strcat(bp, p);
+                       snprintf(bp, SPACELEFT(bp), ", relay=%s",
+                               shortenstring(p, 40));
                }
        }
        bp += strlen(bp);
@@ -1889,6 +2198,7 @@ logdelivery(m, mci, stat, ctladdr, e)
 # define STATLEN       203
 #endif
 
+       /* stat: max 210 bytes */
        if ((bp - buf) > (sizeof buf - ((STATLEN) + 20)))
        {
                /* desperation move -- truncate data */
@@ -1902,9 +2212,10 @@ logdelivery(m, mci, stat, ctladdr, e)
 
        (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) >= l)
+       while (strlen(p) >= (SIZE_T) l)
        {
                register char *q = strchr(p + l, ',');
 
@@ -1950,13 +2261,18 @@ logdelivery(m, mci, stat, ctladdr, e)
        bp = buf;
        sprintf(bp, "delay=%s", pintvl(curtime() - e->e_ctime, TRUE));
        bp += strlen(bp);
+       if (xstart != (time_t) 0)
+       {
+               sprintf(bp, ", xdelay=%s", pintvl(curtime() - xstart, TRUE));
+               bp += strlen(bp);
+       }
 
        if (m != NULL)
        {
                sprintf(bp, ", mailer=%s", m->m_name);
                bp += strlen(bp);
        }
-       syslog(LOG_INFO, "%s: %s", e->e_id, buf);
+       syslog(LOG_INFO, "%s: %.1000s", e->e_id, buf);
 
        buf[0] = '\0';
        if (mci != NULL && mci->mci_host != NULL)
@@ -1965,12 +2281,11 @@ logdelivery(m, mci, stat, ctladdr, e)
                extern SOCKADDR CurHostAddr;
 # endif
 
-               sprintf(buf, "relay=%s", mci->mci_host);
+               sprintf(buf, "relay=%.100s", mci->mci_host);
 
 # ifdef DAEMON
-               (void) strcat(buf, " [");
-               (void) strcat(buf, anynet_ntoa(&CurHostAddr));
-               (void) strcat(buf, "]");
+               if (CurHostAddr.sa.sa_family != 0)
+                       sprintf(bp, " [%.100s]", anynet_ntoa(&CurHostAddr));
 # endif
        }
        else if (strcmp(stat, "queued") != 0)
@@ -1978,15 +2293,17 @@ logdelivery(m, mci, stat, ctladdr, e)
                char *p = macvalue('h', e);
 
                if (p != NULL && p[0] != '\0')
-                       sprintf(buf, "relay=%s", p);
+                       sprintf(buf, "relay=%.100s", p);
        }
        if (buf[0] != '\0')
-               syslog(LOG_INFO, "%s: %s", e->e_id, buf);
+               syslog(LOG_INFO, "%s: %.1000s", e->e_id, buf);
 
        syslog(LOG_INFO, "%s: stat=%s", e->e_id, shortenstring(stat, 63));
 #  endif /* short log buffer */
 # endif /* LOG */
 }
+
+#undef SPACELEFT
 \f/*
 **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
 **
@@ -2008,23 +2325,23 @@ logdelivery(m, mci, stat, ctladdr, e)
 **             outputs some text to fp.
 */
 
+void
 putfromline(mci, e)
        register MCI *mci;
        ENVELOPE *e;
 {
-       char *template = "\201l\n";
+       char *template = UnixFromLine;
        char buf[MAXLINE];
 
        if (bitnset(M_NHDR, mci->mci_mailer->m_flags))
                return;
 
-# ifdef UGLYUUCP
        if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags))
        {
                char *bang;
                char xbuf[MAXLINE];
 
-               expand("\201g", buf, &buf[sizeof buf - 1], e);
+               expand("\201g", buf, sizeof buf, e);
                bang = strchr(buf, '!');
                if (bang == NULL)
                {
@@ -2034,13 +2351,13 @@ putfromline(mci, e)
                else
                {
                        *bang++ = '\0';
-                       (void) sprintf(xbuf, "From %s  \201d remote from %s\n", bang, buf);
+                       (void) sprintf(xbuf, "From %.800s  \201d remote from %.100s\n",
+                               bang, buf);
                        template = xbuf;
                }
        }
-# endif /* UGLYUUCP */
-       expand(template, buf, &buf[sizeof buf - 1], e);
-       putline(buf, mci);
+       expand(template, buf, sizeof buf, e);
+       putxline(buf, mci, PXLF_NOTHINGSPECIAL);
 }
 \f/*
 **  PUTBODY -- put the body of a message.
@@ -2058,6 +2375,12 @@ putfromline(mci, e)
 **             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;
@@ -2069,45 +2392,244 @@ putbody(mci, e, separator)
        **  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 (e->e_df != NULL)
+               if (bitset(MCIF_INHEADER, mci->mci_flags))
                {
-                       e->e_dfp = fopen(e->e_df, "r");
-                       if (e->e_dfp == NULL)
-                               syserr("putbody: Cannot open %s for %s from %s",
-                               e->e_df, e->e_to, e->e_from.q_paddr);
+                       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
-                       putline("<<< No Message Collected >>>", mci);
+               {
+                       e->e_dfdev = stbuf.st_dev;
+                       e->e_dfino = stbuf.st_ino;
+               }
        }
-       if (e->e_dfp != NULL)
+       rewind(e->e_dfp);
+
+#if MIME8TO7
+       if (bitset(MCIF_CVT8TO7, mci->mci_flags))
        {
-               rewind(e->e_dfp);
-               while (!ferror(mci->mci_out) && fgets(buf, sizeof buf, e->e_dfp) != NULL)
+               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)
                {
-                       if (buf[0] == 'F' &&
-                           bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
-                           strncmp(buf, "From ", 5) == 0)
-                               (void) putc('>', mci->mci_out);
-                       if (buf[0] == '-' && buf[1] == '-' && separator != NULL)
+                       sprintf(buf, "Content-Type: text/plain; charset=%s",
+                               defcharset(e));
+                       putline(buf, mci);
+               }
+
+               /* now do the hard work */
+               boundaries[0] = NULL;
+               mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER);
+       }
+       else
+#endif
+       {
+               int ostate;
+               register char *bp;
+               register char *pbp;
+               register int c;
+               int padc;
+               char *buflim;
+               int pos = 0;
+               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];
+
+               /* copy temp file to output with mapping */
+               ostate = OS_HEAD;
+               bp = buf;
+               pbp = peekbuf;
+               while (!ferror(mci->mci_out))
+               {
+                       register char *xp;
+
+                       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)
                        {
-                               /* possible separator */
-                               int sl = strlen(separator);
+                         case OS_HEAD:
+                               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 >>> ", 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);
+                                       pos++;
+                               }
+                               for (xp = buf; xp < bp; xp++)
+                                       putc(*xp, mci->mci_out);
+                               if (c == '\n')
+                               {
+                                       fputs(mci->mci_mailer->m_eol,
+                                             mci->mci_out);
+                                       pos = 0;
+                               }
+                               else
+                               {
+                                       pos += bp - buf;
+                                       if (c != '\r')
+                                               *pbp++ = c;
+                               }
+                               bp = buf;
 
-                               if (strncmp(&buf[2], separator, sl) == 0)
-                                       (void) putc(' ', mci->mci_out);
+                               /* 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);
+                                       if (TrafficLogFile != NULL)
+                                       {
+                                               fputs(mci->mci_mailer->m_eol,
+                                                     TrafficLogFile);
+                                       }
+                                       ostate = OS_HEAD;
+                                       continue;
+                               }
+
+                               /* had a naked carriage return */
+                               *pbp++ = c;
+                               c = '\r';
+                               goto putch;
+
+                         case OS_INLINE:
+                               if (c == '\r')
+                               {
+                                       ostate = OS_CR;
+                                       continue;
+                               }
+putch:
+                               if (mci->mci_mailer->m_linelimit > 0 &&
+                                   pos > mci->mci_mailer->m_linelimit &&
+                                   c != '\n')
+                               {
+                                       putc('!', mci->mci_out);
+                                       fputs(mci->mci_mailer->m_eol, mci->mci_out);
+                                       if (TrafficLogFile != NULL)
+                                       {
+                                               fprintf(TrafficLogFile, "!%s",
+                                                       mci->mci_mailer->m_eol);
+                                       }
+                                       ostate = OS_HEAD;
+                                       *pbp++ = c;
+                                       continue;
+                               }
+                               if (TrafficLogFile != NULL)
+                                       putc(c, TrafficLogFile);
+                               putc(c, mci->mci_out);
+                               pos++;
+                               ostate = c == '\n' ? OS_HEAD : OS_INLINE;
+                               break;
                        }
-                       putline(buf, mci);
                }
 
-               if (ferror(e->e_dfp))
+               /* make sure we are at the beginning of a line */
+               if (bp > buf)
                {
-                       syserr("putbody: %s: read error", e->e_df);
-                       ExitStat = EX_IOERR;
+                       *bp = '\0';
+                       fputs(buf, mci->mci_out);
+                       fputs(mci->mci_mailer->m_eol, mci->mci_out);
                }
        }
 
+       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')
@@ -2139,6 +2661,8 @@ putbody(mci, e, separator)
 **             filename -- the name of the file to send to.
 **             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.
@@ -2147,13 +2671,15 @@ putbody(mci, e, separator)
 **             none.
 */
 
-mailfile(filename, ctladdr, e)
+int
+mailfile(filename, ctladdr, sfflags, e)
        char *filename;
        ADDRESS *ctladdr;
+       int sfflags;
        register ENVELOPE *e;
 {
        register FILE *f;
-       register int pid;
+       register int pid = -1;
        int mode;
 
        if (tTd(11, 1))
@@ -2184,22 +2710,27 @@ mailfile(filename, ctladdr, e)
                int oflags = O_WRONLY|O_APPEND;
 
                if (e->e_lockfp != NULL)
-               {
-                       fclose(e->e_lockfp);
-                       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;
 
+#ifdef HASLSTAT
+               if ((SafeFileEnv != NULL ? lstat(filename, &stb)
+                                        : stat(filename, &stb)) < 0)
+#else
                if (stat(filename, &stb) < 0)
+#endif
                {
                        stb.st_mode = FileMode;
                        oflags |= O_CREAT|O_EXCL;
                }
-               else if (bitset(0111, stb.st_mode))
+               else if (bitset(0111, stb.st_mode) || stb.st_nlink != 1 ||
+                        (SafeFileEnv != NULL && !S_ISREG(stb.st_mode)))
                        exit(EX_CANTCREAT);
                mode = stb.st_mode;
 
@@ -2207,59 +2738,108 @@ mailfile(filename, ctladdr, e)
                errno = 0;
                ExitStat = EX_OK;
 
-               if (ctladdr != NULL)
+               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 && e->e_df != NULL)
+               if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))
                {
-                       e->e_dfp = fopen(e->e_df, "r");
+                       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",
-                                       e->e_df, e->e_to, e->e_from.q_paddr);
+                                       df, e->e_to, e->e_from.q_paddr);
                        }
                }
 
-               if (!bitset(S_ISGID, mode) || setgid(stb.st_gid) < 0)
+               /* select a new user to run as */
+               if (!bitset(SFF_RUNASREALUID, sfflags))
                {
-                       if (ctladdr == NULL || ctladdr->q_uid == 0)
+                       if (bitset(S_ISUID, mode))
                        {
-                               (void) initgroups(DefUser, DefGid);
+                               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 (FileMailer != NULL && FileMailer->m_uid != 0)
+                       {
+                               RealUserName = DefUser;
+                               RealUid = FileMailer->m_uid;
                        }
                        else
                        {
-                               (void) initgroups(ctladdr->q_ruser ?
-                                       ctladdr->q_ruser : ctladdr->q_user,
-                                       ctladdr->q_gid);
+                               RealUserName = DefUser;
+                               RealUid = DefUid;
                        }
+
+                       /* select a new group to run as */
+                       if (bitset(S_ISGID, mode))
+                               RealGid = stb.st_gid;
+                       else if (ctladdr != NULL && ctladdr->q_uid != 0)
+                               RealGid = ctladdr->q_gid;
+                       else if (FileMailer != NULL && FileMailer->m_gid != 0)
+                               RealGid = FileMailer->m_gid;
+                       else
+                               RealGid = DefGid;
                }
-               if (!bitset(S_ISUID, mode) || setuid(stb.st_uid) < 0)
+
+               /* last ditch */
+               if (!bitset(SFF_ROOTOK, sfflags))
                {
-                       if (ctladdr == NULL || ctladdr->q_uid == 0)
-                               (void) setuid(DefUid);
-                       else
-                               (void) setuid(ctladdr->q_uid);
+                       if (RealUid == 0)
+                               RealUid = DefUid;
+                       if (RealGid == 0)
+                               RealGid = DefGid;
+               }
+
+               /* now set the group and user ids */
+               endpwent();
+               if (RealUserName != NULL && !DontInitGroups)
+                       (void) initgroups(RealUserName, RealGid);
+               (void) setgid(RealGid);
+               (void) setuid(RealUid);
+
+               /* 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;
                }
-               FileName = filename;
-               LineNumber = 0;
-               f = dfopen(filename, oflags, FileMode);
+               if (chdir("/") < 0)
+                       syserr("mailfile: cannot chdir(/)");
+
+               sfflags |= SFF_NOPATHCHECK;
+               sfflags &= ~SFF_OPENASROOT;
+               f = safefopen(filename, oflags, FileMode, sfflags);
                if (f == NULL)
                {
                        message("554 cannot open: %s", errstring(errno));
                        exit(EX_CANTCREAT);
                }
-               if (fstat(fileno(f), &fsb) < 0 ||
-                   !bitset(O_CREAT, oflags) &&
-                    (stb.st_nlink != fsb.st_nlink ||
-                     stb.st_dev != fsb.st_dev ||
-                     stb.st_ino != fsb.st_ino ||
-                     stb.st_uid != fsb.st_uid))
+               if (fstat(fileno(f), &stb) < 0)
                {
-                       message("554 cannot write: file changed after open");
+                       message("554 cannot fstat %s", errstring(errno));
                        exit(EX_CANTCREAT);
                }
 
@@ -2270,20 +2850,23 @@ mailfile(filename, ctladdr, e)
                        mcibuf.mci_flags |= MCIF_7BIT;
 
                putfromline(&mcibuf, e);
-               (*e->e_puthdr)(&mcibuf, e);
-               putline("\n", &mcibuf);
+               (*e->e_puthdr)(&mcibuf, e->e_header, e);
                (*e->e_putbody)(&mcibuf, e, NULL);
                putline("\n", &mcibuf);
-               if (ferror(f))
+               if (fflush(f) < 0 || ferror(f))
                {
                        message("451 I/O error: %s", errstring(errno));
                        setstat(EX_IOERR);
                }
-               (void) xfclose(f, "mailfile", filename);
-               (void) fflush(stdout);
 
                /* reset ISUID & ISGID bits for paranoid systems */
+#if HASFCHMOD
+               (void) fchmod(fileno(f), (int) stb.st_mode);
+#else
                (void) chmod(filename, (int) stb.st_mode);
+#endif
+               (void) xfclose(f, "mailfile", filename);
+               (void) fflush(stdout);
                exit(ExitStat);
                /*NOTREACHED*/
        }
@@ -2337,7 +2920,7 @@ hostsignature(m, host, e)
        auto int rcode;
        char *hp;
        char *endp;
-       int oldoptions;
+       int oldoptions = _res.options;
        char *mxhosts[MAXMXHOSTS + 1];
 #endif
 
@@ -2366,10 +2949,7 @@ hostsignature(m, host, e)
 
 #if NAMED_BIND
        if (ConfigLevel < 2)
-       {
-               oldoptions = _res.options;
                _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);   /* XXX */
-       }
 
        for (hp = host; hp != NULL; hp = endp)
        {
@@ -2385,11 +2965,10 @@ hostsignature(m, host, e)
 
                        /* update the connection info for this host */
                        mci = mci_get(hp, m);
+                       mci->mci_lastuse = curtime();
                        mci->mci_exitstat = rcode;
                        mci->mci_errno = errno;
-#if NAMED_BIND
                        mci->mci_herrno = h_errno;
-#endif
 
                        /* and return the original host name as the signature */
                        nmx = 1;
index 913b6f0..ffe1bbf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1986 Eric P. Allman
+ * Copyright (c) 1986, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
 
 #ifndef lint
 #if NAMED_BIND
-static char sccsid[] = "@(#)domain.c   8.19.1.1 (Berkeley) 3/6/95 (with name server)";
+static char sccsid[] = "@(#)domain.c   8.54 (Berkeley) 9/28/95 (with name server)";
 #else
-static char sccsid[] = "@(#)domain.c   8.19.1.1 (Berkeley) 3/6/95 (without name server)";
+static char sccsid[] = "@(#)domain.c   8.54 (Berkeley) 9/28/95 (without name server)";
 #endif
 #endif /* not lint */
 
 #if NAMED_BIND
 
 #include <errno.h>
-#include <arpa/nameser.h>
 #include <resolv.h>
-#include <netdb.h>
 
 typedef union
 {
        HEADER  qb1;
-       char    qb2[PACKETSZ];
+       u_char  qb2[PACKETSZ];
 } querybuf;
 
 static char    MXHostBuf[MAXMXHOSTS*PACKETSZ];
@@ -69,15 +67,17 @@ static char MXHostBuf[MAXMXHOSTS*PACKETSZ];
 # define NO_DATA       NO_ADDRESS
 #endif
 
-#ifndef HEADERSZ
-# define HEADERSZ      sizeof(HEADER)
+#ifndef HFIXEDSZ
+# define HFIXEDSZ      12      /* sizeof(HEADER) */
 #endif
 
-/* don't use sizeof because sizeof(long) is different on 64-bit machines */
-#define SHORTSIZE      2       /* size of a short (really, must be 2) */
-#define LONGSIZE       4       /* size of a long (really, must be 4) */
-
 #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
 \f/*
 **  GETMXRR -- get MX resource records for a domain
 **
@@ -96,13 +96,13 @@ static char MXHostBuf[MAXMXHOSTS*PACKETSZ];
 **                     and 1 is returned.
 */
 
+int
 getmxrr(host, mxhosts, droplocalhost, rcode)
        char *host;
        char **mxhosts;
        bool droplocalhost;
        int *rcode;
 {
-       extern int h_errno;
        register u_char *eom, *cp;
        register int i, j, n;
        int nmx = 0;
@@ -111,11 +111,13 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
        querybuf answer;
        int ancount, qdcount, buflen;
        bool seenlocal = FALSE;
-       u_short pref, localpref, type;
+       u_short pref, type;
+       u_short localpref = 256;
        char *fallbackMX = FallBackMX;
        static bool firsttime = TRUE;
-       STAB *st;
        bool trycanon = FALSE;
+       int (*resfunc)();
+       extern int res_query(), res_search();
        u_short prefer[MAXMXHOSTS];
        int weight[MAXMXHOSTS];
        extern bool getcanonname();
@@ -125,15 +127,14 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
 
        if (fallbackMX != NULL)
        {
-               if (firsttime && res_query(FallBackMX, C_IN, T_A,
-                                          (char *) &answer, sizeof answer) < 0)
+               if (firsttime &&
+                   res_query(FallBackMX, C_IN, T_A,
+                             (u_char *) &answer, sizeof answer) < 0)
                {
                        /* this entry is bogus */
                        fallbackMX = FallBackMX = NULL;
                }
-               else if (droplocalhost &&
-                        (st = stab(fallbackMX, ST_CLASS, ST_FIND)) != NULL &&
-                        bitnset('w', st->s_class))
+               else if (droplocalhost && wordinclass(fallbackMX, 'w'))
                {
                        /* don't use fallback for this pass */
                        fallbackMX = NULL;
@@ -141,12 +142,29 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
                firsttime = FALSE;
        }
 
+       *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 = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer));
+       n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer));
        if (n < 0)
        {
                if (tTd(8, 1))
@@ -163,25 +181,22 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
                        goto punt;
 
                  case HOST_NOT_FOUND:
-#ifdef BROKEN_RES_SEARCH
-                       /* Ultrix resolver returns failure w/ h_errno=0 */
-                 case 0:
+#if BROKEN_RES_SEARCH
+                 case 0:       /* Ultrix resolver retns failure w/ h_errno=0 */
 #endif
-                       /* the host just doesn't exist */
+                       /* host doesn't exist in DNS; might be in /etc/hosts */
+                       trycanon = TRUE;
                        *rcode = EX_NOHOST;
-
-                       if (!UseNameServer)
-                       {
-                               /* might exist in /etc/hosts */
-                               goto punt;
-                       }
-                       break;
+                       goto punt;
 
                  case TRY_AGAIN:
                        /* couldn't connect to the name server */
-                       if (!UseNameServer && errno == ECONNREFUSED)
-                               goto punt;
-
+                       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;
@@ -199,7 +214,7 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
 
        /* find first satisfactory answer */
        hp = (HEADER *)&answer;
-       cp = (u_char *)&answer + HEADERSZ;
+       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)
@@ -210,11 +225,11 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
        while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1)
        {
                if ((n = dn_expand((u_char *)&answer,
-                   eom, cp, (u_char *)bp, buflen)) < 0)
+                   eom, cp, (RES_UNC_T) bp, buflen)) < 0)
                        break;
                cp += n;
                GETSHORT(type, cp);
-               cp += SHORTSIZE + LONGSIZE;
+               cp += INT16SZ + INT32SZ;
                GETSHORT(n, cp);
                if (type != T_MX)
                {
@@ -226,22 +241,25 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
                }
                GETSHORT(pref, cp);
                if ((n = dn_expand((u_char *)&answer, eom, cp,
-                                  (u_char *)bp, buflen)) < 0)
+                                  (RES_UNC_T) bp, buflen)) < 0)
                        break;
                cp += n;
-               if (droplocalhost &&
-                   (st = stab(bp, ST_CLASS, ST_FIND)) != NULL &&
-                   bitnset('w', st->s_class))
+               if (wordinclass(bp, 'w'))
                {
                        if (tTd(8, 3))
                                printf("found localhost (%s) in MX list, pref=%d\n",
                                        bp, pref);
-                       if (!seenlocal || pref < localpref)
-                               localpref = pref;
-                       seenlocal = TRUE;
-                       continue;
+                       if (droplocalhost)
+                       {
+                               if (!seenlocal || pref < localpref)
+                                       localpref = pref;
+                               seenlocal = TRUE;
+                               continue;
+                       }
+                       weight[nmx] = 0;
                }
-               weight[nmx] = mxrand(bp);
+               else
+                       weight[nmx] = mxrand(bp);
                prefer[nmx] = pref;
                mxhosts[nmx++] = bp;
                n = strlen(bp);
@@ -284,11 +302,25 @@ getmxrr(host, mxhosts, droplocalhost, rcode)
                }
        }
 
+       /* 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 || gethostbyname(host) == NULL))
+                   (!TryNullMXList || sm_gethostbyname(host) == NULL))
                {
                        /*
                        **  If we have deleted all MX entries, this is
@@ -337,8 +369,8 @@ punt:
                                *bp++ = '.';
                                *bp = '\0';
                        }
+                       nmx = 1;
                }
-               nmx = 1;
        }
 
        /* if we have a default lowest preference, include that */
@@ -365,6 +397,7 @@ punt:
 **             none.
 */
 
+int
 mxrand(host)
        register char *host;
 {
@@ -392,13 +425,43 @@ mxrand(host)
        }
 
        hfunc &= 0xff;
+       hfunc++;
 
        if (tTd(17, 9))
                printf(" = %d\n", hfunc);
        return hfunc;
 }
 \f/*
-**  GETCANONNAME -- get the canonical name for named host
+**  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.
+*/
+
+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;
+       char *mxhosts[MAXMXHOSTS + 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);
+       else
+               return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av);
+}
+\f/*
+**  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
@@ -420,6 +483,7 @@ mxrand(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.
@@ -427,12 +491,12 @@ mxrand(host)
 */
 
 bool
-getcanonname(host, hbsize, trymx)
+dns_getcanonname(host, hbsize, trymx, statp)
        char *host;
        int hbsize;
        bool trymx;
+       int *statp;
 {
-       extern int h_errno;
        register u_char *eom, *ap;
        register char *cp;
        register int n; 
@@ -445,7 +509,7 @@ getcanonname(host, hbsize, trymx)
        char **dp;
        char *mxmatch;
        bool amatch;
-       bool gotmx;
+       bool gotmx = FALSE;
        int qtype;
        int loopcnt;
        char *xp;
@@ -454,10 +518,13 @@ getcanonname(host, hbsize, trymx)
        extern char *gethostalias();
 
        if (tTd(8, 2))
-               printf("getcanonname(%s)\n", host);
+               printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx);
 
        if ((_res.options & RES_INIT) == 0 && res_init() == -1)
-               return (FALSE);
+       {
+               *statp = EX_UNAVAILABLE;
+               return FALSE;
+       }
 
        /*
        **  Initialize domain search list.  If there is at least one
@@ -471,10 +538,15 @@ getcanonname(host, hbsize, trymx)
 
        loopcnt = 0;
 cnameloop:
-       for (cp = host, n = 0; *cp; cp++)
+       /* 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)
@@ -489,6 +561,17 @@ 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++ = "";
@@ -508,7 +591,8 @@ cnameloop:
        *dp = NULL;
 
        /*
-       **  Now run through the search list for the name in question.
+       **  Now loop through the search list, appending each domain in turn
+       **  name and searching for a match.
        */
 
        mxmatch = NULL;
@@ -519,11 +603,12 @@ cnameloop:
                if (qtype == T_ANY)
                        gotmx = FALSE;
                if (tTd(8, 5))
-                       printf("getcanonname: trying %s.%s (%s)\n", host, *dp,
+                       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, sizeof(answer));
+                                     answer.qb2, sizeof(answer.qb2));
                if (ret <= 0)
                {
                        if (tTd(8, 7))
@@ -534,6 +619,7 @@ cnameloop:
                        {
                                /* the name server seems to be down */
                                h_errno = TRY_AGAIN;
+                               *statp = EX_TEMPFAIL;
                                return FALSE;
                        }
 
@@ -552,13 +638,7 @@ cnameloop:
                                }
                        }
 
-                       if (mxmatch != NULL)
-                       {
-                               /* we matched before -- use that one */
-                               break;
-                       }
-
-                       /* otherwise, try the next name */
+                       /* definite no -- try the next domain */
                        dp++;
                        qtype = T_ANY;
                        continue;
@@ -567,13 +647,13 @@ cnameloop:
                        printf("\tYES\n");
 
                /*
-               **  This might be a bogus match.  Search for A or
-               **  CNAME records.  If we don't have a matching
+               **  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 + HEADERSZ;
+               ap = (u_char *) &answer + HFIXEDSZ;
                eom = (u_char *) &answer + ret;
 
                /* skip question part of response -- we know what we asked */
@@ -584,44 +664,66 @@ cnameloop:
                                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)
+               for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom;
+                                                                       ap += n)
                {
                        n = dn_expand((u_char *) &answer, eom, ap,
-                                     (u_char *) nbuf, sizeof nbuf);
+                                     (RES_UNC_T) nbuf, sizeof nbuf);
                        if (n < 0)
                                break;
                        ap += n;
                        GETSHORT(type, ap);
-                       ap += SHORTSIZE + LONGSIZE;
+                       ap += INT16SZ + INT32SZ;
                        GETSHORT(n, ap);
                        switch (type)
                        {
                          case T_MX:
                                gotmx = TRUE;
-                               if (**dp != '\0')
+                               if (**dp != '\0' && HasWildcardMX)
                                {
-                                       /* got a match -- save that info */
+                                       /*
+                                       **  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;
                                }
 
-                               /* exact MX matches are as good as an A match */
-                               /* fall through */
+                               /*
+                               **  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:
-                               /* good show */
+                               /* 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*/
@@ -631,17 +733,18 @@ cnameloop:
                                        {
                                                char ebuf[MAXLINE];
 
-                                               sprintf(ebuf, "Deferred: DNS failure: CNAME loop for %s",
+                                               sprintf(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, (u_char *)nbuf, sizeof(nbuf))) < 0)
+                                   eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0)
                                        break;
                                (void)strncpy(host, nbuf, hbsize); /* XXX */
                                host[hbsize - 1] = '\0';
@@ -662,14 +765,24 @@ cnameloop:
 
                if (amatch)
                {
-                       /* got an A record and no CNAME */
+                       /* 
+                       **  Got a good match -- either an A, CNAME, or an
+                       **  exact MX record.  Save it and get out of here.
+                       */
+
                        mxmatch = *dp;
                        break;
                }
 
                /*
-               **  If this was a T_ANY query, we may have the info but
-               **  need an explicit query.  Try T_A, then T_MX.
+               **  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)
@@ -678,39 +791,51 @@ cnameloop:
                        qtype = T_MX;
                else
                {
-                       /* really nothing in this domain; try the next */
                        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.
+       */
 
-       /* create matching name and return */
        (void) sprintf(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;
+       register char *p = NULL;
        char buf[MAXLINE];
        static char hbuf[MAXDNAME];
 
        fname = getenv("HOSTALIASES");
-       if (fname == NULL || (fp = fopen(fname, "r")) == NULL)
+       if (fname == NULL ||
+           (fp = safefopen(fname, O_RDONLY, 0, SFF_REGONLY)) == NULL)
                return NULL;
-       setbuf(fp, NULL);
        while (fgets(buf, sizeof buf, fp) != NULL)
        {
                for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++)
@@ -744,28 +869,4 @@ gethostalias(host)
        return hbuf;
 }
 
-
-#else /* not NAMED_BIND */
-
-#include <netdb.h>
-
-bool
-getcanonname(host, hbsize, trymx)
-       char *host;
-       int hbsize;
-       bool trymx;
-{
-       struct hostent *hp;
-
-       hp = gethostbyname(host);
-       if (hp == NULL)
-               return (FALSE);
-
-       if (strlen(hp->h_name) >= hbsize)
-               return (FALSE);
-
-       (void) strcpy(host, hp->h_name);
-       return (TRUE);
-}
-
-#endif /* not NAMED_BIND */
+#endif /* NAMED_BIND */
index e8bb1e4..4bf7ac2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)envelope.c 8.34.1.1 (Berkeley) 2/28/95";
+static char sccsid[] = "@(#)envelope.c 8.76 (Berkeley) 11/11/95";
 #endif /* not lint */
 
 #include "sendmail.h"
-#include <pwd.h>
 
 /*
 **  NEWENVELOPE -- allocate a new envelope
@@ -60,9 +59,6 @@ newenvelope(e, parent)
        register ENVELOPE *e;
        register ENVELOPE *parent;
 {
-       extern putheader(), putbody();
-       extern ENVELOPE BlankEnvelope;
-
        if (e == parent && e->e_parent != NULL)
                parent = e->e_parent;
        clearenvelope(e, TRUE);
@@ -100,16 +96,20 @@ dropenvelope(e)
        register ENVELOPE *e;
 {
        bool queueit = FALSE;
-       bool saveit = bitset(EF_FATALERRS, e->e_flags);
+       bool failure_return = FALSE;
+       bool success_return = FALSE;
        register ADDRESS *q;
        char *id = e->e_id;
        char buf[MAXLINE];
 
        if (tTd(50, 1))
        {
+               extern void printenvflags();
+
                printf("dropenvelope %x: id=", e);
                xputs(e->e_id);
-               printf(", flags=0x%x\n", e->e_flags);
+               printf(", flags=");
+               printenvflags(e);
                if (tTd(50, 10))
                {
                        printf("sendq=");
@@ -117,17 +117,23 @@ dropenvelope(e)
                }
        }
 
+#ifdef LOG
+       if (LogLevel > 84)
+               syslog(LOG_DEBUG, "%s: dropenvelope, e_flags=0x%x, OpMode=%c, pid=%d",
+                       id == NULL ? "[NOQUEUE]" : id,
+                       e->e_flags, OpMode, getpid());
+#endif
+
        /* we must have an id to remove disk files */
        if (id == NULL)
                return;
 
-#ifdef LOG
+       /* 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);
-       if (LogLevel > 84)
-               syslog(LOG_DEBUG, "dropenvelope, id=%s, flags=0x%x, pid=%d",
-                                 id, e->e_flags, getpid());
-#endif /* LOG */
        e->e_flags &= ~EF_LOGSENDER;
 
        /* post statistics */
@@ -140,69 +146,116 @@ dropenvelope(e)
        e->e_flags &= ~EF_QUEUERUN;
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
-               if (bitset(QQUEUEUP, q->q_flags))
+               if (!bitset(QBADADDR|QDONTSEND|QSENT, q->q_flags) ||
+                   bitset(QQUEUEUP, q->q_flags))
                        queueit = TRUE;
-               if (!bitset(QDONTSEND, q->q_flags) &&
-                   bitset(QBADADDR, q->q_flags))
+
+               /* see if a notification is needed */
+               if (bitset(QBADADDR, q->q_flags) &&
+                   bitset(QPINGONFAILURE, q->q_flags))
                {
-                       if (q->q_owner == NULL &&
-                           strcmp(e->e_from.q_paddr, "<>") != 0)
+                       failure_return = TRUE;
+                       if (q->q_owner == NULL && !emptyaddr(&e->e_from))
                                (void) sendtolist(e->e_from.q_paddr, NULL,
-                                                 &e->e_errorqueue, e);
+                                                 &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 (curtime() > e->e_ctime + TimeOuts.to_q_return)
+       else if (curtime() > e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass])
        {
                (void) sprintf(buf, "Cannot send message for %s",
-                       pintvl(TimeOuts.to_q_return, FALSE));
+                       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;
-               saveit = TRUE;
+               failure_return = TRUE;
                fprintf(e->e_xfp, "Message could not be delivered for %s\n",
-                       pintvl(TimeOuts.to_q_return, FALSE));
+                       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(QQUEUEUP, q->q_flags))
+                       if (bitset(QQUEUEUP, q->q_flags) ||
+                           !bitset(QBADADDR|QDONTSEND|QSENT, q->q_flags))
+                       {
                                q->q_flags |= QBADADDR;
+                               q->q_status = "4.4.7";
+                       }
                }
        }
-       else if (TimeOuts.to_q_warning > 0 &&
-           curtime() > e->e_ctime + TimeOuts.to_q_warning)
+       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) &&
+               bool delay_return = FALSE;
+
+               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 &&
+                   !bitset(EF_WARNING|EF_RESPONSE, e->e_flags) &&
                    e->e_class >= 0 &&
-                   strcmp(e->e_from.q_paddr, "<>") != 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))
                {
                        (void) sprintf(buf,
-                               "warning: cannot send message for %s",
-                               pintvl(TimeOuts.to_q_warning, FALSE));
+                               "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;
-                       saveit = TRUE;
+                       failure_return = TRUE;
                }
                fprintf(e->e_xfp,
                        "Warning: message still undelivered after %s\n",
-                       pintvl(TimeOuts.to_q_warning, FALSE));
+                       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, FALSE));
+                       pintvl(TimeOuts.to_q_return[e->e_timeoutclass], FALSE));
+       }
+
+       if (tTd(50, 2))
+               printf("failure_return=%d success_return=%d queueit=%d\n",
+                       failure_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)
+       {
+               failure_return = TRUE;
                for (q = e->e_sendqueue; q != NULL; q = q->q_next)
                {
-                       if (bitset(QQUEUEUP, q->q_flags))
-                               q->q_flags |= QREPORT;
+                       if (!bitset(QDONTSEND, q->q_flags))
+                               q->q_flags |= QBADADDR;
                }
        }
 
@@ -210,33 +263,42 @@ dropenvelope(e)
        **  Send back return receipts as requested.
        */
 
+/*
        if (e->e_receiptto != NULL && bitset(EF_SENDRECEIPT, e->e_flags)
            && !bitset(PRIV_NORECEIPTS, PrivacyFlags))
+*/
+       if (e->e_receiptto == NULL)
+               e->e_receiptto = e->e_from.q_paddr;
+       if (success_return && !failure_return &&
+           !bitset(PRIV_NORECEIPTS, PrivacyFlags) &&
+           strcmp(e->e_receiptto, "<>") != 0)
        {
                auto ADDRESS *rlist = NULL;
 
-               (void) sendtolist(e->e_receiptto, NULLADDR, &rlist, e);
+               e->e_flags |= EF_SENDRECEIPT;
+               (void) sendtolist(e->e_receiptto, NULLADDR, &rlist, 0, e);
                (void) returntosender("Return receipt", rlist, FALSE, e);
-               e->e_flags &= ~EF_SENDRECEIPT;
        }
+       e->e_flags &= ~EF_SENDRECEIPT;
 
        /*
        **  Arrange to send error messages if there are fatal errors.
        */
 
-       if (saveit && e->e_errormode != EM_QUIET)
-               savemail(e);
+       if (failure_return && e->e_errormode != EM_QUIET)
+               savemail(e, !bitset(EF_NO_BODY_RETN, e->e_flags));
 
        /*
        **  Arrange to send warning messages to postmaster as requested.
        */
 
-       if (bitset(EF_PM_NOTIFY, e->e_flags) && PostMasterCopy != NULL &&
+       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;
 
-               (void) sendtolist(PostMasterCopy, NULLADDR, &rlist, e);
+               (void) sendtolist(PostMasterCopy, NULLADDR, &rlist, 0, e);
                (void) returntosender(e->e_message, rlist, FALSE, e);
        }
 
@@ -244,13 +306,18 @@ dropenvelope(e)
        **  Instantiate or deinstantiate the queue.
        */
 
-       if ((!queueit && !bitset(EF_KEEPQUEUE, e->e_flags)) ||
-           bitset(EF_CLRQUEUE, e->e_flags))
+simpledrop:
+       if (!queueit || bitset(EF_CLRQUEUE, e->e_flags))
        {
                if (tTd(50, 1))
-                       printf("\n===== Dropping [dq]f%s =====\n\n", e->e_id);
-               if (e->e_df != NULL)
-                       xunlink(e->e_df);
+               {
+                       extern void printenvflags();
+
+                       printf("\n===== Dropping [dq]f%s... queueit=%d, e_flags=",
+                               e->e_id, queueit);
+                       printenvflags(e);
+               }
+               xunlink(queuename(e, 'd'));
                xunlink(queuename(e, 'q'));
 
 #ifdef LOG
@@ -261,7 +328,7 @@ dropenvelope(e)
        else if (queueit || !bitset(EF_INQUEUE, e->e_flags))
        {
 #ifdef QUEUE
-               queueup(e, bitset(EF_KEEPQUEUE, e->e_flags), FALSE);
+               queueup(e, FALSE);
 #else /* QUEUE */
                syserr("554 dropenvelope: queueup");
 #endif /* QUEUE */
@@ -273,9 +340,10 @@ dropenvelope(e)
 
        /* make sure that this envelope is marked unused */
        if (e->e_dfp != NULL)
-               (void) xfclose(e->e_dfp, "dropenvelope", e->e_df);
+               (void) xfclose(e->e_dfp, "dropenvelope df", e->e_id);
        e->e_dfp = NULL;
-       e->e_id = e->e_df = NULL;
+       e->e_id = NULL;
+       e->e_flags &= ~EF_HAS_DF;
 }
 \f/*
 **  CLEARENVELOPE -- clear an envelope without unlocking
@@ -312,7 +380,7 @@ clearenvelope(e, fullclear)
                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_df);
+                       (void) xfclose(e->e_dfp, "clearenvelope dfp", e->e_id);
                e->e_xfp = e->e_dfp = NULL;
        }
 
@@ -359,7 +427,6 @@ initsys(e)
 #endif /* TTYNAME */
        extern char *ttyname();
        extern void settime();
-       extern char Version[];
 
        /*
        **  Give this envelope a reality.
@@ -491,9 +558,12 @@ openxscript(e)
        }
        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);
@@ -573,7 +643,6 @@ setsender(from, e, delimptr, internal)
        char *bp;
        char buf[MAXNAME + 2];
        char pvpbuf[PSBUFSIZE];
-       extern struct passwd *getpwnam();
        extern char *FullName;
 
        if (tTd(45, 1))
@@ -614,9 +683,12 @@ setsender(from, e, delimptr, internal)
                        if (p == NULL)
                        {
                                char *host = RealHostName;
+
                                if (host == NULL)
                                        host = MyHostName;
-                               (void) sprintf(ebuf, "%s@%s", realname, host);
+                               (void) sprintf(ebuf, "%.*s@%.*s",
+                                       MAXNAME, realname,
+                                       MAXNAME, host);
                                p = ebuf;
                        }
                        syslog(LOG_NOTICE,
@@ -629,6 +701,7 @@ setsender(from, e, delimptr, internal)
                        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;
@@ -640,7 +713,7 @@ setsender(from, e, delimptr, internal)
                        char nbuf[100];
 
                        SuprErrs = TRUE;
-                       expand("\201n", nbuf, &nbuf[sizeof nbuf], e);
+                       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,
@@ -658,37 +731,30 @@ setsender(from, e, delimptr, internal)
        }
        SuprErrs = FALSE;
 
-       pvp = NULL;
-       if (e->e_from.q_mailer == LocalMailer)
+# if USERDB
+       if (bitnset(M_CHECKUDB, e->e_from.q_mailer->m_flags))
        {
-# ifdef USERDB
                register char *p;
                extern char *udbsender();
-# endif
 
+               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 has given fullname already, don't redefine */
+                       /* if the user already given fullname don't redefine */
                        if (FullName == NULL)
                                FullName = macvalue('x', e);
                        if (FullName != NULL && FullName[0] == '\0')
                                FullName = NULL;
-
-# ifdef USERDB
-                       p = udbsender(e->e_from.q_user);
-
-                       if (p != NULL)
-                       {
-                               /*
-                               **  We have an alternate address for the sender
-                               */
-
-                               pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL);
-                       }
-# endif /* USERDB */
                }
 
-               if ((pw = getpwnam(e->e_from.q_user)) != NULL)
+               if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL)
                {
                        /*
                        **  Process passwd file entry.
@@ -738,14 +804,14 @@ setsender(from, e, delimptr, internal)
        **      links in the net.
        */
 
-       if (pvp == NULL)
-               pvp = prescan(from, delimchar, pvpbuf, sizeof pvpbuf, NULL);
+       pvp = prescan(from, delimchar, pvpbuf, sizeof pvpbuf, NULL, NULL);
        if (pvp == NULL)
        {
                /* don't need to give error -- prescan did that already */
 # ifdef LOG
                if (LogLevel > 2)
-                       syslog(LOG_NOTICE, "cannot prescan from (%s)", from);
+                       syslog(LOG_NOTICE, "cannot prescan from (%s)",
+                               shortenstring(from, 203));
 # endif
                finis();
        }
@@ -754,7 +820,7 @@ setsender(from, e, delimptr, internal)
        (void) rewrite(pvp, 4, 0, e);
        bp = buf + 1;
        cataddr(pvp, NULL, bp, sizeof buf - 2, '\0');
-       if (*bp == '@')
+       if (*bp == '@' && !bitnset(M_NOBRACKET, e->e_from.q_mailer->m_flags))
        {
                /* heuristic: route-addr: add angle brackets */
                strcat(bp, ">");
@@ -767,11 +833,92 @@ setsender(from, e, delimptr, internal)
        if (e->e_from.q_mailer != NULL &&
            bitnset(M_CANONICAL, e->e_from.q_mailer->m_flags))
        {
+               char **lastat;
                extern char **copyplist();
 
-               while (*pvp != NULL && strcmp(*pvp, "@") != 0)
-                       pvp++;
-               if (*pvp != NULL)
-                       e->e_fromdomain = copyplist(pvp, TRUE);
+               /* 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);
+                       }
+               }
+       }
+}
+\f/*
+**  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");
 }
index c6a87e9..241a486 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)err.c      8.27 (Berkeley) 4/18/94";
+static char sccsid[] = "@(#)err.c      8.42 (Berkeley) 11/29/95";
 #endif /* not lint */
 
 # include "sendmail.h"
 # include <errno.h>
-# include <netdb.h>
-# include <pwd.h>
 
 /*
 **  SYSERR -- Print error message.
@@ -53,8 +51,11 @@ static char sccsid[] = "@(#)err.c    8.27 (Berkeley) 4/18/94";
 **     be used lightly.
 **
 **     Parameters:
-**             f -- the format string
-**             a, b, c, d, e -- 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
@@ -65,9 +66,12 @@ static char sccsid[] = "@(#)err.c    8.27 (Berkeley) 4/18/94";
 **             sets ExitStat.
 */
 
-char   MsgBuf[BUFSIZ*2];       /* text of most recent message */
+char   MsgBuf[BUFSIZ*2];               /* text of most recent message */
+char   HeldMessageBuf[sizeof MsgBuf];  /* for held messages */
 
-static void    fmtmsg();
+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
@@ -107,6 +111,14 @@ syserr(fmt, va_alist)
        VA_END;
        puterrmsg(MsgBuf);
 
+       /* save this message for mailq printing */
+       if (!panic)
+       {
+               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)
        {
@@ -119,7 +131,7 @@ syserr(fmt, va_alist)
        }
 
 # ifdef LOG
-       pw = getpwuid(getuid());
+       pw = sm_getpwuid(getuid());
        if (pw != NULL)
                uname = pw->pw_name;
        else
@@ -129,7 +141,7 @@ syserr(fmt, va_alist)
        }
 
        if (LogLevel > 0)
-               syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %s",
+               syslog(panic ? LOG_ALERT : LOG_CRIT, "%s: SYSERR(%s): %.900s",
                        CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
                        uname, &MsgBuf[4]);
 # endif /* LOG */
@@ -143,6 +155,8 @@ syserr(fmt, va_alist)
 #ifdef XLA
                xla_all_end();
 #endif
+               if (tTd(0, 1))
+                       abort();
                exit(EX_OSERR);
        }
        errno = 0;
@@ -155,7 +169,9 @@ syserr(fmt, va_alist)
 **     This is much like syserr except it is for user errors.
 **
 **     Parameters:
-**             fmt, a, b, c, d -- printf strings
+**             fmt -- the format string.  If it does not begin with
+**                     a three-digit SMTP reply code, 501 is assumed.
+**             (others) -- printf strings
 **
 **     Returns:
 **             none
@@ -183,11 +199,41 @@ usrerr(fmt, va_alist)
        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];
+
+                       sprintf(buf, "Postmaster warning: %.*s",
+                               sizeof buf - 22, MsgBuf + 4);
+                       CurEnv->e_message = newstr(buf);
+               }
+               else
+               {
+                       CurEnv->e_message = newstr(MsgBuf + 4);
+               }
+               break;
+       }
+
        puterrmsg(MsgBuf);
 
 # ifdef LOG
        if (LogLevel > 3 && LogUsrErrs)
-               syslog(LOG_NOTICE, "%s: %s",
+               syslog(LOG_NOTICE, "%s: %.900s",
                        CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
                        &MsgBuf[4]);
 # endif /* LOG */
@@ -201,7 +247,7 @@ usrerr(fmt, va_alist)
 **     Parameters:
 **             msg -- the message (printf fmt) -- it can begin with
 **                     an SMTP reply code.  If not, 050 is assumed.
-**             a, b, c, d, e -- printf arguments
+**             (others) -- printf arguments
 **
 **     Returns:
 **             none
@@ -226,7 +272,23 @@ message(msg, va_alist)
        VA_START(msg);
        fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
        VA_END;
-       putoutmsg(MsgBuf, FALSE);
+       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;
+       }
 }
 \f/*
 **  NMESSAGE -- print message (not necessarily an error)
@@ -234,10 +296,10 @@ message(msg, va_alist)
 **     Just like "message" except it never puts the to... tag on.
 **
 **     Parameters:
-**             num -- the default ARPANET error number (in ascii)
 **             msg -- the message (printf fmt) -- if it begins
-**                     with three digits, this number overrides num.
-**             a, b, c, d, e -- printf arguments
+**                     with a three digit SMTP reply code, that is used,
+**                     otherwise 050 is assumed.
+**             (others) -- printf arguments
 **
 **     Returns:
 **             none
@@ -262,7 +324,23 @@ nmessage(msg, va_alist)
        VA_START(msg);
        fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
        VA_END;
-       putoutmsg(MsgBuf, FALSE);
+       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;
+       }
 }
 \f/*
 **  PUTOUTMSG -- output error message to transcript and channel
@@ -271,6 +349,8 @@ nmessage(msg, va_alist)
 **             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.
@@ -281,25 +361,42 @@ nmessage(msg, va_alist)
 **             Deletes SMTP reply code number as appropriate.
 */
 
-putoutmsg(msg, holdmsg)
+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\n", msg, holdmsg ? " (held)" : "");
+               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 (CurEnv->e_xfp != NULL && strchr("456", msg[0]) != NULL)
+       if (!heldmsg && CurEnv->e_xfp != NULL && strchr("45", msg[0]) != NULL)
                fprintf(CurEnv->e_xfp, "%s\n", msg);
 
+       if (msgcode == '8')
+               msg[0] = '0';
+
        /* output to channel if appropriate */
-       if (holdmsg || (!Verbose && msg[0] == '0'))
+       if (!Verbose && msg[0] == '0')
                return;
-
-       /* map warnings to something SMTP can handle */
-       if (msg[0] == '6')
-               msg[0] = '5';
+       if (holdmsg)
+       {
+               /* save for possible future display */
+               msg[0] = msgcode;
+               strcpy(HeldMessageBuf, msg);
+               return;
+       }
 
        (void) fflush(stdout);
 
@@ -334,7 +431,7 @@ putoutmsg(msg, holdmsg)
                        "%s: SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
                        CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
                        CurHostName == NULL ? "NO-HOST" : CurHostName,
-                       msg, errstring(errno));
+                       shortenstring(msg, 203), errstring(errno));
 #endif
 }
 \f/*
@@ -350,13 +447,14 @@ putoutmsg(msg, holdmsg)
 **             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);
+       putoutmsg(msg, HoldErrs, FALSE);
 
        /* signal the error */
        Errors++;
@@ -392,14 +490,16 @@ puterrmsg(msg)
 static void
 fmtmsg(eb, to, num, eno, fmt, ap)
        register char *eb;
-       char *to;
-       char *num;
+       const char *to;
+       const char *num;
        int eno;
-       char *fmt;
+       const char *fmt;
        va_list ap;
 {
        char del;
        char *meb;
+       int l;
+       int spaceleft = sizeof MsgBuf;
 
        /* output the reply code */
        if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
@@ -413,18 +513,23 @@ fmtmsg(eb, to, num, eno, fmt, ap)
                del = ' ';
        (void) sprintf(eb, "%3.3s%c", num, del);
        eb += 4;
+       spaceleft -= 4;
 
        /* output the file name and line number */
        if (FileName != NULL)
        {
-               (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
-               eb += strlen(eb);
+               (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')
        {
-               (void) sprintf(eb, "%s... ", shortenstring(to, 203));
+               (void) snprintf(eb, spaceleft, "%s... ",
+                       shortenstring(to, 203));
+               spaceleft -= strlen(eb);
                while (*eb != '\0')
                        *eb++ &= 0177;
        }
@@ -432,23 +537,50 @@ fmtmsg(eb, to, num, eno, fmt, ap)
        meb = eb;
 
        /* output the message */
-       (void) vsprintf(eb, fmt, ap);
+       (void) vsnprintf(eb, spaceleft, fmt, ap);
+       spaceleft -= strlen(eb);
        while (*eb != '\0')
                *eb++ &= 0177;
 
        /* output the error code, if any */
        if (eno != 0)
-       {
-               (void) sprintf(eb, ": %s", errstring(eno));
-               eb += strlen(eb);
-       }
+               (void) snprintf(eb, spaceleft, ": %s", errstring(eno));
+}
+\f/*
+**  BUFFER_ERRORS -- arrange to buffer future error messages
+**
+**     Parameters:
+**             none
+**
+**     Returns:
+**             none.
+*/
 
-       if (num[0] == '5' || (CurEnv->e_message == NULL && num[0] == '4'))
-       {
-               if (CurEnv->e_message != NULL)
-                       free(CurEnv->e_message);
-               CurEnv->e_message = newstr(meb);
-       }
+void
+buffer_errors()
+{
+       HeldMessageBuf[0] = '\0';
+       HoldErrs = TRUE;
+}
+\f/*
+**  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;
 }
 \f/*
 **  ERRSTRING -- return string description of error code
@@ -505,13 +637,15 @@ errstring(errnum)
          case EHOSTDOWN:
                if (CurHostName == NULL)
                        break;
-               (void) sprintf(buf, "Host %s is down", CurHostName);
+               (void) sprintf(buf, "Host %s is down",
+                       shortenstring(CurHostName, 203));
                return (buf);
 
          case ECONNREFUSED:
                if (CurHostName == NULL)
                        break;
-               (void) sprintf(buf, "Connection refused by %s", CurHostName);
+               (void) sprintf(buf, "Connection refused by %s",
+                       shortenstring(CurHostName, 203));
                return (buf);
 # endif
 
index 8493e79..ae242b1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)headers.c  8.32 (Berkeley) 4/14/94";
+static char sccsid[] = "@(#)headers.c  8.82 (Berkeley) 10/28/95";
 #endif /* not lint */
 
 # include <errno.h>
@@ -47,6 +47,7 @@ static char sccsid[] = "@(#)headers.c 8.32 (Berkeley) 4/14/94";
 **     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:
@@ -57,9 +58,11 @@ static char sccsid[] = "@(#)headers.c        8.32 (Berkeley) 4/14/94";
 **             Contents of 'line' are destroyed.
 */
 
-chompheader(line, def, e)
+int
+chompheader(line, def, hdrp, e)
        char *line;
        bool def;
+       HDR **hdrp;
        register ENVELOPE *e;
 {
        register char *p;
@@ -69,11 +72,20 @@ chompheader(line, def, e)
        char *fvalue;
        struct hdrinfo *hi;
        bool cond = FALSE;
+       bool headeronly;
        BITMAP mopts;
-       char buf[MAXNAME];
+       char buf[MAXNAME + 1];
 
        if (tTd(31, 6))
-               printf("chompheader: %s\n", line);
+       {
+               printf("chompheader: ");
+               xputs(line);
+               printf("\n");
+       }
+
+       headeronly = hdrp != NULL;
+       if (!headeronly)
+               hdrp = &e->e_header;
 
        /* strip off options */
        clrbitmap(mopts);
@@ -91,7 +103,7 @@ chompheader(line, def, e)
                        p = q;
                }
                else
-                       usrerr("553 header syntax error, line \"%s\"", line);
+                       syserr("553 header syntax error, line \"%s\"", line);
                cond = TRUE;
        }
 
@@ -114,6 +126,10 @@ chompheader(line, def, e)
        if (*fvalue == ' ')
                fvalue++;
 
+       /* security scan: long field names are end-of-header */
+       if (strlen(fname) > 100)
+               return H_EOH;
+
        /* see if it is a known type */
        for (hi = HdrInfo; hi->hi_field != NULL; hi++)
        {
@@ -126,17 +142,38 @@ chompheader(line, def, e)
                if (hi->hi_field == NULL)
                        printf("no header match\n");
                else
-                       printf("header match, hi_flags=%o\n", hi->hi_flags);
+                       printf("header match, hi_flags=%x\n", hi->hi_flags);
        }
 
        /* see if this is a resent message */
-       if (!def && bitset(H_RESENT, hi->hi_flags))
+       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 (bitset(H_EOH, hi->hi_flags))
                return (hi->hi_flags);
 
+#ifdef LOTUS_NOTES_HACK
+       /*
+       **  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 = ' ';
+       }
+#endif
+
        /*
        **  Drop explicit From: if same as what we would generate.
        **  This is to make MH (which doesn't always give a full name)
@@ -146,7 +183,8 @@ chompheader(line, def, e)
        p = "resent-from";
        if (!bitset(EF_RESENT, e->e_flags))
                p += 7;
-       if (!def && !bitset(EF_QUEUERUN, e->e_flags) && strcasecmp(fname, p) == 0)
+       if (!def && !headeronly && !bitset(EF_QUEUERUN, e->e_flags) &&
+           strcasecmp(fname, p) == 0)
        {
                if (tTd(31, 2))
                {
@@ -158,7 +196,7 @@ chompheader(line, def, e)
                     strcmp(fvalue, e->e_from.q_user) == 0))
                        return (hi->hi_flags);
 #ifdef MAYBENEXTRELEASE                /* XXX UNTESTED XXX UNTESTED XXX UNTESTED XXX */
-#ifdef USERDB
+#if USERDB
                else
                {
                        auto ADDRESS a;
@@ -183,13 +221,13 @@ chompheader(line, def, e)
                                SuprErrs = TRUE;
                        fancy = crackaddr(fvalue);
                        if (parseaddr(fvalue, &a, RF_COPYNONE, '\0', NULL, e) != NULL &&
-                           a.q_mailer == LocalMailer &&
+                           bitnset(M_CHECKUDB, a.q_mailer->m_flags) &&
                            (p = udbsender(a.q_user)) != NULL)
                        {
                                char *oldg = macvalue('g', e);
 
                                define('g', p, e);
-                               expand(fancy, buf, &buf[sizeof buf], e);
+                               expand(fancy, buf, sizeof buf, e);
                                define('g', oldg, e);
                                fvalue = buf;
                        }
@@ -200,12 +238,20 @@ chompheader(line, def, e)
        }
 
        /* delete default value for this header */
-       for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
+       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 */
@@ -222,7 +268,7 @@ chompheader(line, def, e)
                h->h_flags |= H_CHECK;
 
        /* hack to see if this is a new format message */
-       if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
+       if (!def && !headeronly && bitset(H_RCPT|H_FROM, h->h_flags) &&
            (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL ||
             strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL))
        {
@@ -239,7 +285,7 @@ chompheader(line, def, e)
 **     Parameters:
 **             field -- the name of the header field.
 **             value -- the value of the field.
-**             e -- the envelope to add them to.
+**             hp -- an indirect pointer to the header structure list.
 **
 **     Returns:
 **             none.
@@ -248,10 +294,11 @@ chompheader(line, def, e)
 **             adds the field on the list of headers for this envelope.
 */
 
-addheader(field, value, e)
+void
+addheader(field, value, hdrlist)
        char *field;
        char *value;
-       ENVELOPE *e;
+       HDR **hdrlist;
 {
        register HDR *h;
        register struct hdrinfo *hi;
@@ -265,7 +312,7 @@ addheader(field, value, e)
        }
 
        /* find current place in list -- keep back pointer? */
-       for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
+       for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link)
        {
                if (strcasecmp(field, h->h_field) == 0)
                        break;
@@ -288,7 +335,7 @@ addheader(field, value, e)
 **
 **     Parameters:
 **             field -- the field name.
-**             e -- the envelope containing the header.
+**             header -- the header list.
 **
 **     Returns:
 **             pointer to the value part.
@@ -299,13 +346,13 @@ addheader(field, value, e)
 */
 
 char *
-hvalue(field, e)
+hvalue(field, header)
        char *field;
-       register ENVELOPE *e;
+       HDR *header;
 {
        register HDR *h;
 
-       for (h = e->e_header; h != NULL; h = h->h_link)
+       for (h = header; h != NULL; h = h->h_link)
        {
                if (!bitset(H_DEFAULT, h->h_flags) &&
                    strcasecmp(h->h_field, field) == 0)
@@ -319,11 +366,17 @@ hvalue(field, e)
 **     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:
-**             s -- string to check for possible headerness.
+**             h -- string to check for possible headerness.
 **
 **     Returns:
-**             TRUE if s is a header.
+**             TRUE if h is a header.
 **             FALSE otherwise.
 **
 **     Side Effects:
@@ -331,12 +384,20 @@ hvalue(field, e)
 */
 
 bool
-isheader(s)
-       register char *s;
+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++;
@@ -349,7 +410,9 @@ isheader(s)
 **     Parameters:
 **             e -- the envelope to process.
 **             full -- if set, do full processing (e.g., compute
-**                     message priority).
+**                     message priority).  This should not be set
+**                     when reading a queue file because some info
+**                     needed to compute the priority is wrong.
 **
 **     Returns:
 **             none.
@@ -360,6 +423,7 @@ isheader(s)
 **             Aborts the message if the hop count is exceeded.
 */
 
+void
 eatheader(e, full)
        register ENVELOPE *e;
        bool full;
@@ -382,28 +446,42 @@ eatheader(e, full)
                define('u', NULL, e);
 
        /* full name of from person */
-       p = hvalue("full-name", e);
+       p = hvalue("full-name", e->e_header);
        if (p != NULL)
                define('x', p, e);
 
        if (tTd(32, 1))
                printf("----- collected header -----\n");
-       msgid = "<none>";
+       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("%s: <NULL>\n", h->h_field);
+                               printf("<NULL>\n");
                        continue;
                }
 
                /* do early binding */
                if (bitset(H_DEFAULT, h->h_flags))
                {
-                       expand(h->h_value, buf, &buf[sizeof buf], e);
+                       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();
+
+                                       expand(crackaddr(buf), buf, sizeof buf, e);
+                               }
                                h->h_value = newstr(buf);
                                h->h_flags &= ~H_DEFAULT;
                        }
@@ -411,7 +489,6 @@ eatheader(e, full)
 
                if (tTd(32, 1))
                {
-                       printf("%s: ", h->h_field);
                        xputs(h->h_value);
                        printf("\n");
                }
@@ -428,7 +505,7 @@ eatheader(e, full)
                        int saveflags = e->e_flags;
 
                        (void) sendtolist(h->h_value, NULLADDR,
-                                         &e->e_sendqueue, e);
+                                         &e->e_sendqueue, 0, e);
 
                        /* delete fatal errors generated by this address */
                        if (!GrabTo && !bitset(EF_FATALERRS, saveflags))
@@ -436,7 +513,10 @@ eatheader(e, full)
                }
 
                /* save the message-id for logging */
-               if (full && strcasecmp(h->h_field, "message-id") == 0)
+               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))
@@ -446,11 +526,6 @@ eatheader(e, full)
                /* see if this is a return-receipt header */
                if (bitset(H_RECEIPTTO, h->h_flags))
                        e->e_receiptto = h->h_value;
-
-               /* see if this is an errors-to header */
-               if (UseErrorsTo && bitset(H_ERRORSTO, h->h_flags))
-                       (void) sendtolist(h->h_value, NULLADDR,
-                                         &e->e_errorqueue, e);
        }
        if (tTd(32, 1))
                printf("----------------------------\n");
@@ -464,21 +539,60 @@ eatheader(e, full)
                e->e_hopcount = hopcnt;
 
        /* message priority */
-       p = hvalue("precedence", e);
+       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"))
+                       e->e_timeoutclass = TOC_URGENT;
+               else if (strcasecmp(p, "normal"))
+                       e->e_timeoutclass = TOC_NORMAL;
+               else if (strcasecmp(p, "non-urgent"))
+                       e->e_timeoutclass = TOC_NONURGENT;
+       }
 
        /* date message originated */
-       p = hvalue("posted-date", e);
+       p = hvalue("posted-date", e->e_header);
        if (p == NULL)
-               p = hvalue("date", e);
+               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)
@@ -493,7 +607,7 @@ eatheader(e, full)
                        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)) != NULL)
+                           (p = hvalue(hi->hi_field, e->e_header)) != NULL)
                                break;
                }
                if (hi->hi_field != NULL)
@@ -510,7 +624,7 @@ eatheader(e, full)
        */
 
 # ifdef LOG
-       if (full && LogLevel > 4)
+       if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
                logsender(e, msgid);
 # endif /* LOG */
        e->e_flags &= ~EF_LOGSENDER;
@@ -526,6 +640,7 @@ eatheader(e, full)
 **             none
 */
 
+void
 logsender(e, msgid)
        register ENVELOPE *e;
        char *msgid;
@@ -534,8 +649,23 @@ logsender(e, msgid)
        char *name;
        register char *sbp;
        register char *p;
-       char hbuf[MAXNAME];
-       char sbuf[MAXLINE];
+       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]";
@@ -552,7 +682,7 @@ logsender(e, msgid)
                if (RealHostAddr.sa.sa_family != 0)
                {
                        p = &hbuf[strlen(hbuf)];
-                       (void) sprintf(p, " (%s)",
+                       (void) sprintf(p, " (%.100s)",
                                anynet_ntoa(&RealHostAddr));
                }
        }
@@ -561,12 +691,12 @@ logsender(e, msgid)
 #  if (SYSLOG_BUFSIZE) >= 256
        sbp = sbuf;
        sprintf(sbp, "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d",
-           e->e_from.q_paddr, e->e_msgsize, e->e_class,
-           e->e_msgpriority, e->e_nrcpts);
+           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)
        {
-               sprintf(sbp, ", msgid=%.100s", msgid);
+               sprintf(sbp, ", msgid=%.100s", mbuf);
                sbp += strlen(sbp);
        }
        if (e->e_bodytype != NULL)
@@ -577,33 +707,35 @@ logsender(e, msgid)
        p = macvalue('r', e);
        if (p != NULL)
                (void) sprintf(sbp, ", proto=%.20s", p);
-       syslog(LOG_INFO, "%s: %s, relay=%s",
+       syslog(LOG_INFO, "%s: %.850s, relay=%.100s",
            e->e_id, sbuf, name);
 
 #  else                        /* short syslog buffer */
 
        syslog(LOG_INFO, "%s: from=%s",
-               e->e_id, shortenstring(e->e_from.q_paddr, 83));
+               e->e_id, e->e_from.q_paddr == NULL ? "<NONE>" :
+                               shortenstring(e->e_from.q_paddr, 83));
        syslog(LOG_INFO, "%s: size=%ld, class=%ld, pri=%ld, nrcpts=%d",
                e->e_id, e->e_msgsize, e->e_class,
                e->e_msgpriority, e->e_nrcpts);
        if (msgid != NULL)
-               syslog(LOG_INFO, "%s: msgid=%s", e->e_id, msgid);
+               syslog(LOG_INFO, "%s: msgid=%s",
+                       e->e_id, shortenstring(mbuf, 83));
        sbp = sbuf;
        sprintf(sbp, "%s:", e->e_id);
        sbp += strlen(sbp);
        if (e->e_bodytype != NULL)
        {
-               sprintf(sbp, " bodytype=%s,", e->e_bodytype);
+               sprintf(sbp, " bodytype=%.20s,", e->e_bodytype);
                sbp += strlen(sbp);
        }
        p = macvalue('r', e);
        if (p != NULL)
        {
-               sprintf(sbp, " proto=%s,", p);
+               sprintf(sbp, " proto=%.20s,", p);
                sbp += strlen(sbp);
        }
-       syslog(LOG_INFO, "%s relay=%s", sbuf, name);
+       syslog(LOG_INFO, "%.400s relay=%.100s", sbuf, name);
 #  endif
 # endif
 }
@@ -620,6 +752,7 @@ logsender(e, msgid)
 **             none.
 */
 
+int
 priencode(p)
        char *p;
 {
@@ -678,9 +811,12 @@ crackaddr(addr)
        bool putgmac = FALSE;
        bool quoteit = FALSE;
        bool gotangle = FALSE;
+       bool gotcolon = FALSE;
        register char *bp;
        char *buflim;
-       static char buf[MAXNAME];
+       char *bufhead;
+       char *addrhead;
+       static char buf[MAXNAME + 1];
 
        if (tTd(33, 1))
                printf("crackaddr(%s)\n", addr);
@@ -694,9 +830,9 @@ crackaddr(addr)
        **  adjusted later if we find them.
        */
 
-       bp = buf;
+       bp = bufhead = buf;
        buflim = &buf[sizeof buf - 5];
-       p = addr;
+       p = addrhead = addr;
        copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0;
        qmode = realqmode = FALSE;
 
@@ -780,8 +916,80 @@ crackaddr(addr)
                                bp--;
                }
 
+               /* check for group: list; syntax */
+               if (c == ':' && anglelev <= 0 && !gotcolon && !ColonOkInAddr)
+               {
+                       register char *q;
+
+                       if (*p == ':')
+                       {
+                               /* special case -- :: syntax */
+                               if (cmtlev <= 0 && !qmode)
+                                       quoteit = TRUE;
+                               if (copylev > 0 && !skipping)
+                               {
+                                       *bp++ = c;
+                                       *bp++ = c;
+                               }
+                               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(".'@,;:\\()[]", c) != NULL)
+               if (strchr(MustQuoteChars, c) != NULL)
                {
                        /*
                        **  If these occur as the phrase part of a <>
@@ -809,7 +1017,7 @@ crackaddr(addr)
                        if (!skipping)
                                realanglelev = 1;
 
-                       bp = buf;
+                       bp = bufhead;
                        if (quoteit)
                        {
                                *bp++ = '"';
@@ -820,7 +1028,7 @@ crackaddr(addr)
                                        continue;
                                p++;
                        }
-                       for (q = addr; q < p; )
+                       for (q = addrhead; q < p; )
                        {
                                c = *q++;
                                if (bp < buflim)
@@ -901,6 +1109,7 @@ crackaddr(addr)
 **
 **     Parameters:
 **             mci -- the connection information.
+**             h -- the header to put.
 **             e -- envelope to use.
 **
 **     Returns:
@@ -917,27 +1126,45 @@ crackaddr(addr)
 # define MAX(a,b) (((a)>(b))?(a):(b))
 #endif
 
-putheader(mci, e)
+void
+putheader(mci, h, e)
        register MCI *mci;
+       register HDR *h;
        register ENVELOPE *e;
 {
        char buf[MAX(MAXLINE,BUFSIZ)];
-       register HDR *h;
        char obuf[MAXLINE];
 
        if (tTd(34, 1))
                printf("--- putheader, mailer = %s ---\n",
                        mci->mci_mailer->m_name);
 
-       for (h = e->e_header; h != NULL; h = h->h_link)
+       mci->mci_flags |= MCIF_INHEADER;
+       for (; h != NULL; h = h->h_link)
        {
-               register char *p;
+               register char *p = h->h_value;
                extern bool bitintersect();
 
                if (tTd(34, 11))
                {
                        printf("  %s: ", h->h_field);
-                       xputs(h->h_value);
+                       xputs(p);
+               }
+
+               /* suppress Content-Transfer-Encoding: if we are MIMEing */
+               if (bitset(H_CTE, h->h_flags) &&
+                   bitset(MCIF_CVT8TO7|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");
+                       goto vanilla;
                }
 
                if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
@@ -966,10 +1193,9 @@ putheader(mci, e)
                }
 
                /* macro expand value if generated internally */
-               p = h->h_value;
                if (bitset(H_DEFAULT, h->h_flags))
                {
-                       expand(p, buf, &buf[sizeof buf], e);
+                       expand(p, buf, sizeof buf, e);
                        p = buf;
                        if (p == NULL || *p == '\0')
                        {
@@ -979,6 +1205,23 @@ putheader(mci, e)
                        }
                }
 
+               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) sprintf(obuf, "%s:", h->h_field);
+                               putline(obuf, mci);
+                       }
+                       continue;
+               }
+
                if (tTd(34, 11))
                        printf("\n");
 
@@ -995,21 +1238,51 @@ putheader(mci, e)
                {
                        /* vanilla header line */
                        register char *nlp;
+                       register char *obp;
 
-                       (void) sprintf(obuf, "%s: ", h->h_field);
+vanilla:
+                       (void) sprintf(obuf, "%.200s: ", h->h_field);
+                       obp = obuf + strlen(obuf);
                        while ((nlp = strchr(p, '\n')) != NULL)
                        {
+
                                *nlp = '\0';
-                               (void) strcat(obuf, p);
+                               sprintf(obp, "%.*s",
+                                       sizeof obuf - (obp - obuf) - 1, p);
                                *nlp = '\n';
                                putline(obuf, mci);
                                p = ++nlp;
-                               obuf[0] = '\0';
+                               obp = obuf;
                        }
-                       (void) strcat(obuf, p);
+                       sprintf(obp, "%.*s", sizeof obuf - (obp - obuf) - 1, p);
+                       putline(obuf, 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, 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)
+               {
+                       sprintf(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
 }
 \f/*
 **  COMMAIZE -- output a header field, making a comma-translated list.
@@ -1051,7 +1324,7 @@ commaize(h, p, oldstyle, mci, e)
                printf("commaize(%s: %s)\n", h->h_field, p);
 
        obp = obuf;
-       (void) sprintf(obp, "%s: ", h->h_field);
+       (void) sprintf(obp, "%.200s: ", h->h_field);
        opos = strlen(h->h_field) + 2;
        obp += opos;
        omax = mci->mci_mailer->m_linelimit - 2;
@@ -1088,7 +1361,7 @@ commaize(h, p, oldstyle, mci, e)
                        char pvpbuf[PSBUFSIZE];
 
                        (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf,
-                                      sizeof pvpbuf, &oldp);
+                                      sizeof pvpbuf, &oldp, NULL);
                        p = oldp;
 
                        /* look to see if we have an at sign */
@@ -1119,6 +1392,18 @@ commaize(h, p, oldstyle, mci, e)
                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();
+                       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')
index 8a8a90b..94ef834 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)macro.c    8.3 (Berkeley) 2/7/94";
+static char sccsid[] = "@(#)macro.c    8.13 (Berkeley) 7/10/95";
 #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.
-**             buflim -- the buffer limit, i.e., the address
-**                     of the last usable position in buf.
+**             bufsize -- the size of the buffer.
 **             e -- envelope in which to work.
 **
 **     Returns:
@@ -56,10 +59,10 @@ static char sccsid[] = "@(#)macro.c 8.3 (Berkeley) 2/7/94";
 */
 
 void
-expand(s, buf, buflim, e)
+expand(s, buf, bufsize, e)
        register char *s;
        register char *buf;
-       char *buflim;
+       size_t bufsize;
        register ENVELOPE *e;
 {
        register char *xp;
@@ -115,7 +118,7 @@ expand(s, buf, buflim, e)
                        continue;
 
                  case MACROEXPAND:     /* macro interpolation */
-                       c = *++s & 0177;
+                       c = *++s & 0377;
                        if (c != '\0')
                                q = macvalue(c, e);
                        else
@@ -160,14 +163,14 @@ expand(s, buf, buflim, e)
        /* recurse as appropriate */
        if (recurse)
        {
-               expand(xbuf, buf, buflim, e);
+               expand(xbuf, buf, bufsize, e);
                return;
        }
 
        /* copy results out */
-       i = buflim - buf - 1;
-       if (i > xp - xbuf)
-               i = xp - xbuf;
+       i = xp - xbuf;
+       if (i >= bufsize)
+               i = bufsize - 1;
        bcopy(xbuf, buf, i);
        buf[i] = '\0';
 }
@@ -242,11 +245,12 @@ define(n, v, e)
 {
        if (tTd(35, 9))
        {
-               printf("define(%c as ", n);
+               printf("%sdefine(%s as ", 
+                   (e->e_macro[n & 0377] == NULL) ? "" : "re", macname(n));
                xputs(v);
                printf(")\n");
        }
-       e->e_macro[n & 0177] = v;
+       e->e_macro[n & 0377] = v;
 }
 \f/*
 **  MACVALUE -- return uninterpreted value of a macro.
@@ -266,7 +270,7 @@ macvalue(n, e)
        int n;
        register ENVELOPE *e;
 {
-       n &= 0177;
+       n &= 0377;
        while (e != NULL)
        {
                register char *p = e->e_macro[n];
@@ -277,3 +281,161 @@ macvalue(n, e)
        }
        return (NULL);
 }
+\f/*
+**  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;
+}
+\f/*
+**  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;
+}
+\f/*
+**  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);
+}
index a0253a2..5e352f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -39,17 +39,15 @@ static char copyright[] =
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)main.c     8.55.1.7 (Berkeley) 3/5/95";
+static char sccsid[] = "@(#)main.c     8.162 (Berkeley) 11/18/95";
 #endif /* not lint */
 
 #define        _DEFINE
 
 #include "sendmail.h"
 #if NAMED_BIND
-#include <arpa/nameser.h>
 #include <resolv.h>
 #endif
-#include <pwd.h>
 
 # ifdef lint
 char   edata, end;
@@ -94,21 +92,9 @@ ENVELOPE     BlankEnvelope;  /* a "blank" envelope */
 ENVELOPE       MainEnvelope;   /* the envelope around the basic letter */
 ADDRESS                NullAddress =   /* a null address */
                { "", "", NULL, "" };
-char           *UserEnviron[MAXUSERENVIRON + 2];
-                               /* saved user environment */
-char           RealUserName[256];      /* the actual user id on this host */
 char           *CommandLineArgs;       /* command line args for pid file */
 bool           Warn_Q_option = FALSE;  /* warn about Q option use */
-
-/*
-**  Pointers for setproctitle.
-**     This allows "ps" listings to give more useful information.
-*/
-
-# ifdef SETPROCTITLE
-char           **Argv = NULL;          /* pointer to argument vector */
-char           *LastArgv = NULL;       /* end of argv */
-# endif /* SETPROCTITLE */
+char           **SaveArgv;     /* argument vector for re-execing */
 
 static void    obsolete();
 
@@ -118,8 +104,9 @@ ERROR %%%%   Cannot have daemon mode without SMTP   %%%% ERROR
 #endif /* SMTP */
 #endif /* DAEMON */
 
-#define MAXCONFIGLEVEL 5       /* highest config version level known */
+#define MAXCONFIGLEVEL 6       /* highest config version level known */
 
+int
 main(argc, argv, envp)
        int argc;
        char **argv;
@@ -127,7 +114,6 @@ main(argc, argv, envp)
 {
        register char *p;
        char **av;
-       extern int finis();
        extern char Version[];
        char *ep, *from;
        typedef int (*fnptr)();
@@ -139,22 +125,25 @@ main(argc, argv, envp)
        bool warn_C_flag = FALSE;
        char warn_f_flag = '\0';
        static bool reenter = FALSE;
-       char *argv0 = argv[0];
        struct passwd *pw;
        struct stat stb;
+       struct hostent *hp;
        char jbuf[MAXHOSTNAMELEN];      /* holds MyHostName */
+       static char rnamebuf[MAXNAME];  /* holds RealUserName */
        extern int DtableSize;
        extern int optind;
+       extern int opterr;
        extern time_t convtime();
-       extern putheader(), putbody();
        extern void intsig();
-       extern char **myhostname();
+       extern struct hostent *myhostname();
        extern char *arpadate();
        extern char *getauthinfo();
        extern char *getcfname();
        extern char *optarg;
        extern char **environ;
        extern void sigusr1();
+       extern void sighup();
+       extern void initmacros __P((ENVELOPE *));
 
        /*
        **  Check to see if we reentered.
@@ -172,8 +161,8 @@ main(argc, argv, envp)
        /* do machine-dependent initializations */
        init_md(argc, argv);
 
-       /* arrange to dump state on signal */
 #ifdef SIGUSR1
+       /* arrange to dump state on user-1 signal */
        setsignal(SIGUSR1, sigusr1);
 #endif
 
@@ -213,6 +202,8 @@ main(argc, argv, envp)
 # endif
 #endif 
 
+       tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
+
        /* set up the blank envelope */
        BlankEnvelope.e_puthdr = putheader;
        BlankEnvelope.e_putbody = putbody;
@@ -231,25 +222,29 @@ main(argc, argv, envp)
        RealUid = getuid();
        RealGid = getgid();
 
-       pw = getpwuid(RealUid);
+       pw = sm_getpwuid(RealUid);
        if (pw != NULL)
-               (void) strcpy(RealUserName, pw->pw_name);
+               (void) strcpy(rnamebuf, pw->pw_name);
        else
-               (void) sprintf(RealUserName, "Unknown UID %d", RealUid);
+               (void) sprintf(rnamebuf, "Unknown UID %d", RealUid);
+       RealUserName = rnamebuf;
 
        /* 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; *av != NULL; )
+       for (av = argv, i = 0; *av != NULL; )
        {
+               SaveArgv[i++] = newstr(*av);
                if (av != argv)
                        *p++ = ' ';
                strcpy(p, *av++);
                p += strlen(p);
        }
+       SaveArgv[i] = NULL;
 
        /* Handle any non-getoptable constructions. */
        obsolete(argv);
@@ -259,63 +254,98 @@ main(argc, argv, envp)
        */
 
 #if defined(__osf__) || defined(_AIX3)
-# define OPTIONS       "B:b:C:cd:e:F:f:h:Iimno:p:q:r:sTtvX:x"
+# define OPTIONS       "B:b:C:cd:e:F:f:h:IiM:mnO:o:p:q:r:sTtvX:x"
 #endif
-#if defined(ultrix)
-# define OPTIONS       "B:b:C:cd:e:F:f:h:IiM:mno:p:q:r:sTtvX:"
-#endif
-#if defined(NeXT)
-# define OPTIONS       "B:b:C:cd:e:F:f:h:IimnOo:p:q:r:sTtvX:"
+#if defined(sony_news)
+# define OPTIONS       "B:b:C:cd:E:e:F:f:h:IiJ:M:mnO:o:p:q:r:sTtvX:"
 #endif
 #ifndef OPTIONS
-# define OPTIONS       "B:b:C:cd:e:F:f:h:Iimno:p:q:r:sTtvX:"
+# define OPTIONS       "B:b:C:cd:e:F:f:h:IiM:mnO:o:p:q:r:sTtvX:"
 #endif
+       opterr = 0;
        while ((j = getopt(argc, argv, OPTIONS)) != EOF)
        {
                switch (j)
                {
                  case 'd':
-                       tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
                        tTflag(optarg);
                        setbuf(stdout, (char *) NULL);
-                       printf("Version %s\n", Version);
                        break;
                }
        }
+       opterr = 1;
 
-       InChannel = stdin;
-       OutChannel = stdout;
-
-       /*
-       **  Move the environment so setproctitle can use the space at
-       **  the top of memory.
-       */
+       if (tTd(0, 1))
+       {
+               int ll;
+               extern char *CompileOptions[];
 
-       for (i = j = 0; j < MAXUSERENVIRON && (p = envp[i]) != NULL; i++)
+               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))
        {
-               if (strncmp(p, "IFS=", 4) == 0 || strncmp(p, "LD_", 3) == 0)
-                       continue;
-               UserEnviron[j++] = newstr(p);
+               int ll;
+               extern char *OsCompileOptions[];
+
+               printf("    OS Defines:", Version);
+               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("   Config file:\t%s\n", getcfname());
+               printf("      Pid file:\t%s\n", PidFile);
        }
-       UserEnviron[j] = NULL;
-       environ = UserEnviron;
 
-# ifdef SETPROCTITLE
-       /*
-       **  Save start and extent of argv for setproctitle.
-       */
+       InChannel = stdin;
+       OutChannel = stdout;
 
-       Argv = argv;
-       if (i > 0)
-               LastArgv = envp[i - 1] + strlen(envp[i - 1]);
-       else
-               LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
-# endif /* SETPROCTITLE */
+       /* initialize for setproctitle */
+       initsetproctitle(argc, argv, envp);
+
+       /* prime the child environment */
+       setuserenv("AGENT", "sendmail");
 
        if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
                (void) setsignal(SIGINT, intsig);
-       if (setsignal(SIGHUP, SIG_IGN) != SIG_IGN)
-               (void) setsignal(SIGHUP, intsig);
        (void) setsignal(SIGTERM, intsig);
        (void) setsignal(SIGPIPE, SIG_IGN);
        OldUmask = umask(022);
@@ -335,12 +365,13 @@ main(argc, argv, envp)
 
        /* initialize some macros, etc. */
        initmacros(CurEnv);
+       init_vendor_macros(CurEnv);
 
        /* version */
        define('v', Version, CurEnv);
 
        /* hostname */
-       av = myhostname(jbuf, sizeof jbuf);
+       hp = myhostname(jbuf, sizeof jbuf);
        if (jbuf[0] != '\0')
        {
                struct  utsname utsname;
@@ -357,7 +388,6 @@ main(argc, argv, envp)
                        if (p[1] != '\0')
                        {
                                define('m', newstr(&p[1]), CurEnv);
-                               setclass('m', &p[1]);
                        }
                        while (p != NULL && strchr(&p[1], '.') != NULL)
                        {
@@ -378,19 +408,42 @@ main(argc, argv, envp)
                        p = jbuf;
                }
                if (tTd(0, 4))
-                       printf("UUCP nodename: %s\n", p);
+                       printf(" UUCP nodename: %s\n", p);
                p = newstr(p);
                define('k', p, CurEnv);
                setclass('k', p);
                setclass('w', p);
        }
-       while (av != NULL && *av != NULL)
+       if (hp != NULL)
        {
-               if (tTd(0, 4))
-                       printf("\ta.k.a.: %s\n", *av);
-               setclass('w', *av++);
+               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)
+               {
+                       register int i;
+
+                       for (i = 0; hp->h_addr_list[i] != NULL; i++)
+                       {
+                               char ipbuf[103];
+
+                               sprintf(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
        }
 
+       /* probe interfaces and locate any additional names */
+       load_if_names();
+
        /* current time */
        define('b', arpadate((char *) NULL), CurEnv);
 
@@ -447,9 +500,7 @@ main(argc, argv, envp)
                          case MD_TEST:
                          case MD_INITALIAS:
                          case MD_PRINT:
-#ifdef MAYBE_NEXT_RELEASE
                          case MD_ARPAFTP:
-#endif
                                OpMode = j;
                                break;
 
@@ -466,17 +517,14 @@ main(argc, argv, envp)
                        break;
 
                  case 'B':     /* body type */
-                       if (strcasecmp(optarg, "7bit") == 0 ||
-                           strcasecmp(optarg, "8bitmime") == 0)
-                               CurEnv->e_bodytype = newstr(optarg);
-                       else
-                               usrerr("Illegal body type %s", optarg);
+                       CurEnv->e_bodytype = optarg;
                        break;
 
                  case 'C':     /* select configuration file (already done) */
                        if (RealUid != 0)
                                warn_C_flag = TRUE;
                        ConfFile = optarg;
+                       endpwent();
                        (void) setgid(RealGid);
                        (void) setuid(RealUid);
                        safecf = FALSE;
@@ -520,6 +568,10 @@ main(argc, argv, envp)
                        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)
@@ -574,6 +626,7 @@ main(argc, argv, envp)
                        break;
 
                  case 'X':     /* traffic log file */
+                       endpwent();
                        setgid(RealGid);
                        setuid(RealUid);
                        TrafficLogFile = fopen(optarg, "a");
@@ -599,9 +652,7 @@ main(argc, argv, envp)
                        break;
 
                  case 'e':     /* error message disposition */
-# if defined(ultrix)
                  case 'M':     /* define macro */
-# endif
                        setoption(j, optarg, FALSE, TRUE, CurEnv);
                        break;
 
@@ -619,8 +670,10 @@ main(argc, argv, envp)
                  case 'x':     /* random flag that OSF/1 & AIX mailx passes */
                        break;
 # endif
-# if defined(NeXT)
-                 case 'O':     /* random flag that NeXT Mail.app passes */
+# if defined(sony_news)
+                 case 'E':
+                 case 'J':     /* ignore flags for Japanese code conversion
+                                  impremented on Sony NEWS */
                        break;
 # endif
 
@@ -638,23 +691,42 @@ main(argc, argv, envp)
        **      Extract special fields for local use.
        */
 
-#ifdef XDEBUG
+       /* 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);
+       vendor_post_defaults(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);
 
        if (tTd(0, 1))
        {
-               printf("SYSTEM IDENTITY (after readcf):");
-               printf("\n\t    (short domain name) $w = ");
+               printf("\n============ SYSTEM IDENTITY (after readcf) ============");
+               printf("\n      (short domain name) $w = ");
                xputs(macvalue('w', CurEnv));
-               printf("\n\t(canonical domain name) $j = ");
+               printf("\n  (canonical domain name) $j = ");
                xputs(macvalue('j', CurEnv));
-               printf("\n\t       (subdomain name) $m = ");
+               printf("\n         (subdomain name) $m = ");
                xputs(macvalue('m', CurEnv));
-               printf("\n\t            (node name) $k = ");
+               printf("\n              (node name) $k = ");
                xputs(macvalue('k', CurEnv));
-               printf("\n");
+               printf("\n========================================================\n\n");
        }
 
        /*
@@ -662,47 +734,48 @@ main(argc, argv, envp)
        */
 
 #if NAMED_BIND
-       if (!bitset(RES_INIT, _res.options))
+       if (UseNameServer && !bitset(RES_INIT, _res.options))
                res_init();
+# ifdef RES_NOALIASES
+       _res.options |= RES_NOALIASES;
+# endif
 #endif
 
        /*
-       **  Process authorization warnings from command line.
+       **  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_f_flag != '\0')
-               auth_warning(CurEnv, "%s set sender to %s using -%c",
-                       RealUserName, from, warn_f_flag);
-*/
        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;
+       }
+
        /* Enforce use of local time (null string overrides this) */
        if (TimeZoneSpec == NULL)
                unsetenv("TZ");
        else if (TimeZoneSpec[0] != '\0')
-       {
-               char **evp = UserEnviron;
-               char tzbuf[100];
-
-               strcpy(tzbuf, "TZ=");
-               strcpy(&tzbuf[3], TimeZoneSpec);
-
-               while (*evp != NULL && strncmp(*evp, "TZ=", 3) != 0)
-                       evp++;
-               if (*evp == NULL)
-               {
-                       *evp++ = newstr(tzbuf);
-                       *evp = NULL;
-               }
-               else
-                       *evp++ = newstr(tzbuf);
-       }
+               setuserenv("TZ", TimeZoneSpec);
+       else
+               setuserenv("TZ", NULL);
+       tzset();
 
+       /* check for sane configuration level */
        if (ConfigLevel > MAXCONFIGLEVEL)
        {
                syserr("Warning: .cf version level (%d) exceeds program functionality (%d)",
@@ -712,32 +785,24 @@ main(argc, argv, envp)
        if (MeToo)
                BlankEnvelope.e_flags |= EF_METOO;
 
-# ifdef QUEUE
-       if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
+       switch (OpMode)
        {
-               struct stat stbuf;
+         case MD_DAEMON:
+               /* remove things that don't make sense in daemon mode */
+               FullName = NULL;
+               GrabTo = FALSE;
 
-               /* check to see if we own the queue directory */
-               if (stat(QueueDir, &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 */
+               /* arrange to restart on hangup signal */
+               setsignal(SIGHUP, sighup);
+               break;
 
-       switch (OpMode)
-       {
          case MD_INITALIAS:
                Verbose = TRUE;
-               break;
+               /* fall through... */
 
-         case MD_DAEMON:
-               /* remove things that don't make sense in daemon mode */
-               FullName = NULL;
+         default:
+               /* arrange to exit cleanly on hangup signal */
+               setsignal(SIGHUP, intsig);
                break;
        }
 
@@ -760,31 +825,56 @@ main(argc, argv, envp)
                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, &jbuf[sizeof jbuf - 1], CurEnv);
+       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)
-               syserr("No local mailer defined");
-       else
+       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)
@@ -792,6 +882,39 @@ main(argc, argv, envp)
        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);
+
+               /* propogate some envariables into children */
+               setuserenv("ISP", NULL);
+               setuserenv("SYSTYPE", NULL);
+       }
+
+       /* MIME Content-Types that cannot be transfer encoded */
+       setclass('n', "multipart/signed");
+
+       /* MIME message/* 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");
 
        /* operate in queue directory */
        if (OpMode != MD_TEST && chdir(QueueDir) < 0)
@@ -800,14 +923,32 @@ main(argc, argv, envp)
                ExitStat = EX_SOFTWARE;
        }
 
+# ifdef 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);
        }
 
-#ifdef XDEBUG
+#if XDEBUG
        checkfd012("before main() initmaps");
 #endif
 
@@ -822,6 +963,7 @@ main(argc, argv, envp)
 #ifdef QUEUE
                dropenvelope(CurEnv);
                printqueue();
+               endpwent();
                setuid(RealUid);
                exit(EX_OK);
 #else /* QUEUE */
@@ -832,10 +974,12 @@ main(argc, argv, envp)
          case MD_INITALIAS:
                /* initialize alias database */
                initmaps(TRUE, CurEnv);
+               endpwent();
                setuid(RealUid);
                exit(EX_OK);
 
          case MD_DAEMON:
+         case MD_SMTP:
                /* don't open alias database -- done in srvrsmtp */
                break;
 
@@ -848,35 +992,12 @@ main(argc, argv, envp)
        if (tTd(0, 15))
        {
                /* print configuration table (or at least part of it) */
-               printrules();
+               if (tTd(0, 90))
+                       printrules();
                for (i = 0; i < MAXMAILERS; i++)
                {
-                       register struct mailer *m = Mailer[i];
-                       int j;
-
-                       if (m == NULL)
-                               continue;
-                       printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld F=", i, 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);
-                       for (j = '\0'; j <= '\177'; j++)
-                               if (bitnset(j, m->m_flags))
-                                       (void) putchar(j);
-                       printf(" E=");
-                       xputs(m->m_eol);
-                       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");
+                       if (Mailer[i] != NULL)
+                               printmailer(Mailer[i]);
                }
        }
 
@@ -894,6 +1015,7 @@ main(argc, argv, envp)
        if (OpMode == MD_TEST)
        {
                char buf[MAXLINE];
+               void intindebug();
 
                if (isatty(fileno(stdin)))
                        Verbose = TRUE;
@@ -903,69 +1025,24 @@ main(argc, argv, envp)
                        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 (;;)
                {
-                       register char **pvp;
-                       char *q;
-                       auto char *delimptr;
-                       extern bool invalidaddr();
-                       extern char *crackaddr();
+                       extern void testmodeline __P((char *, ENVELOPE *));
 
                        if (Verbose)
                                printf("> ");
                        (void) fflush(stdout);
                        if (fgets(buf, sizeof buf, stdin) == NULL)
                                finis();
+                       p = strchr(buf, '\n');
+                       if (p != NULL)
+                               *p = '\0';
                        if (!Verbose)
-                               printf("> %s", buf);
-                       switch (buf[0])
-                       {
-                         case '#':
-                               continue;
-
-#ifdef MAYBENEXTRELEASE
-                         case 'C':             /* try crackaddr */
-                               q = crackaddr(&buf[1]);
-                               xputs(q);
-                               printf("\n");
-                               continue;
-#endif
-                       }
-
-                       for (p = buf; isascii(*p) && isspace(*p); p++)
-                               continue;
-                       q = p;
-                       while (*p != '\0' && !(isascii(*p) && isspace(*p)))
-                               p++;
-                       if (*p == '\0')
-                       {
-                               printf("No address!\n");
-                               continue;
-                       }
-                       *p = '\0';
-                       if (invalidaddr(p + 1, NULL))
-                               continue;
-                       do
-                       {
-                               char pvpbuf[PSBUFSIZE];
-
-                               pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
-                                             &delimptr);
-                               if (pvp == NULL)
-                                       continue;
-                               p = q;
-                               while (*p != '\0')
-                               {
-                                       int stat;
-
-                                       stat = rewrite(pvp, atoi(p), 0, CurEnv);
-                                       if (stat != EX_OK)
-                                               printf("== Ruleset %s status %d\n",
-                                                       p, stat);
-                                       while (*p != '\0' && *p++ != ',')
-                                               continue;
-                               }
-                       } while (*(p = delimptr) != '\0');
+                               printf("> %s\n", buf);
+                       testmodeline(buf, CurEnv);
                }
        }
 
@@ -976,6 +1053,7 @@ main(argc, argv, envp)
 
        if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
        {
+               (void) unsetenv("HOSTALIASES");
                runqueue(FALSE);
                finis();
        }
@@ -994,7 +1072,7 @@ main(argc, argv, envp)
        {
                char dtype[200];
 
-               if (!tTd(0, 1))
+               if (!tTd(99, 100))
                {
                        /* put us in background */
                        i = fork();
@@ -1066,11 +1144,12 @@ main(argc, argv, envp)
        {
                CurEnv->e_sendmode = SM_VERIFY;
                CurEnv->e_errormode = EM_QUIET;
+               PostMasterCopy = NULL;
        }
        else
        {
                /* interactive -- all errors are global */
-               CurEnv->e_flags |= EF_GLOBALERRS;
+               CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
        }
 
        /*
@@ -1078,6 +1157,9 @@ main(argc, argv, envp)
        */
 
        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, FALSE);
        if (macvalue('s', CurEnv) == NULL)
                define('s', RealHostName, CurEnv);
@@ -1089,7 +1171,7 @@ main(argc, argv, envp)
 
                /* collect body for UUCP return */
                if (OpMode != MD_VERIFY)
-                       collect(FALSE, FALSE, CurEnv);
+                       collect(InChannel, FALSE, FALSE, NULL, CurEnv);
                finis();
        }
 
@@ -1111,7 +1193,7 @@ main(argc, argv, envp)
        if (OpMode != MD_VERIFY || GrabTo)
        {
                CurEnv->e_flags |= EF_GLOBALERRS;
-               collect(FALSE, FALSE, CurEnv);
+               collect(InChannel, FALSE, FALSE, NULL, CurEnv);
        }
        errno = 0;
 
@@ -1139,6 +1221,15 @@ main(argc, argv, envp)
 
        finis();
 }
+
+
+void
+intindebug()
+{
+       longjmp(TopFrame, 1);
+}
+
+
 \f/*
 **  FINIS -- Clean up and exit.
 **
@@ -1152,12 +1243,18 @@ main(argc, argv, envp)
 **             exits sendmail
 */
 
+void
 finis()
 {
        if (tTd(2, 1))
-               printf("\n====finis: stat %d e_flags %o, e_id=%s\n",
-                       ExitStat, CurEnv->e_flags,
+       {
+               extern void printenvflags();
+
+               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);
 
@@ -1178,10 +1275,11 @@ finis()
        if (LogLevel > 78)
                syslog(LOG_DEBUG, "finis, pid=%d", getpid());
 # endif /* LOG */
-       if (ExitStat == EX_TEMPFAIL)
+       if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
                ExitStat = EX_OK;
 
        /* reset uid for process accounting */
+       endpwent();
        setuid(RealUid);
 
        exit(ExitStat);
@@ -1212,6 +1310,7 @@ intsig()
 #endif
 
        /* reset uid for process accounting */
+       endpwent();
        setuid(RealUid);
 
        exit(EX_OK);
@@ -1255,12 +1354,18 @@ struct metamac  MetaMacros[] =
        '\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;
-       char buf[5];
        register int c;
+       char buf[5];
+       extern char *MacroName[256];
 
        for (m = MetaMacros; m->metaname != '\0'; m++)
        {
@@ -1277,11 +1382,11 @@ initmacros(e)
        }
 
        /* set defaults for some macros sendmail will use later */
-       define('e', "\201j Sendmail \201v ready at \201b", e);
-       define('l', "From \201g  \201d", e);
        define('n', "MAILER-DAEMON", e);
-       define('o', ".:@[]", e);
-       define('q', "<\201g>", e);
+
+       /* set up external names for some internal macros */
+       MACBINDING("opMode", MID_OPMODE);
+       /*XXX should probably add equivalents for all short macros here XXX*/
 }
 \f/*
 **  DISCONNECT -- remove our connection with any foreground process
@@ -1303,6 +1408,7 @@ initmacros(e)
 **             the controlling tty.
 */
 
+void
 disconnect(droplev, e)
        int droplev;
        register ENVELOPE *e;
@@ -1312,14 +1418,13 @@ disconnect(droplev, e)
        if (tTd(52, 1))
                printf("disconnect: In %d Out %d, e=%x\n",
                        fileno(InChannel), fileno(OutChannel), e);
-       if (tTd(52, 5))
+       if (tTd(52, 100))
        {
                printf("don't\n");
                return;
        }
 
        /* be sure we don't get nasty signals */
-       (void) setsignal(SIGHUP, SIG_IGN);
        (void) setsignal(SIGINT, SIG_IGN);
        (void) setsignal(SIGQUIT, SIG_IGN);
 
@@ -1363,7 +1468,7 @@ disconnect(droplev, e)
                errno = 0;
        }
 
-#ifdef XDEBUG
+#if XDEBUG
        checkfd012("disconnect");
 #endif
 
@@ -1391,7 +1496,11 @@ obsolete(argv)
                /* skip over options that do have a value */
                op = strchr(OPTIONS, ap[1]);
                if (op != NULL && *++op == ':' && ap[2] == '\0' &&
-                   ap[1] != 'd' && argv[1] != NULL && argv[1][0] != '-')
+                   ap[1] != 'd' &&
+#if defined(sony_news)
+                   ap[1] != 'E' && ap[1] != 'J' &&
+#endif
+                   argv[1] != NULL && argv[1][0] != '-')
                {
                        argv++;
                        continue;
@@ -1414,6 +1523,16 @@ obsolete(argv)
                /* 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
        }
 }
 \f/*
@@ -1445,7 +1564,7 @@ auth_warning(e, msg, va_alist)
        {
                register char *p;
                static char hostbuf[48];
-               extern char **myhostname();
+               extern struct hostent *myhostname();
 
                if (hostbuf[0] == '\0')
                        (void) myhostname(hostbuf, sizeof hostbuf);
@@ -1453,10 +1572,67 @@ auth_warning(e, msg, va_alist)
                (void) sprintf(buf, "%s: ", hostbuf);
                p = &buf[strlen(buf)];
                VA_START(msg);
-               vsprintf(p, msg, ap);
+               vsnprintf(p, sizeof buf - (p - buf), msg, ap);
                VA_END;
-               addheader("X-Authentication-Warning", buf, e);
+               addheader("X-Authentication-Warning", buf, &e->e_header);
+#ifdef LOG
+               if (LogLevel > 3)
+                       syslog(LOG_INFO, "%s: Authentication-Warning: %.400s",
+                               e->e_id == NULL ? "[NOQUEUE]" : e->e_id, buf);
+#endif
+       }
+}
+\f/*
+**  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 = getenv(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);
 }
 \f/*
 **  DUMPSTATE -- dump state
@@ -1470,17 +1646,16 @@ dumpstate(when)
 {
 #ifdef LOG
        register char *j = macvalue('j', CurEnv);
-       register STAB *s;
 
        syslog(LOG_DEBUG, "--- dumping state on %s: $j = %s ---",
                when,
                j == NULL ? "<NULL>" : j);
        if (j != NULL)
        {
-               s = stab(j, ST_CLASS, ST_FIND);
-               if (s == NULL || !bitnset('w', s->s_class))
+               if (!wordinclass(j, 'w'))
                        syslog(LOG_DEBUG, "*** $j not in $=w ***");
        }
+       syslog(LOG_DEBUG, "CurChildren = %d", CurChildren);
        syslog(LOG_DEBUG, "--- open file descriptors: ---");
        printopenfds(TRUE);
        syslog(LOG_DEBUG, "--- connection cache: ---");
@@ -1508,3 +1683,431 @@ sigusr1()
 {
        dumpstate("user signal");
 }
+
+
+void
+sighup()
+{
+#ifdef LOG
+       if (LogLevel > 3)
+               syslog(LOG_INFO, "restarting %s on signal", SaveArgv[0]);
+#endif
+       releasesignal(SIGHUP);
+       execv(SaveArgv[0], (ARGV_T) SaveArgv);
+#ifdef LOG
+       if (LogLevel > 0)
+               syslog(LOG_ALERT, "could not exec %s: %m", SaveArgv[0]);
+#endif
+       exit(EX_OSFILE);
+}
+\f/*
+**  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 *));
+
+       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)
+                               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);
+                       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)
+               {
+                       auto int rcode = EX_OK;
+                       char host[MAXHOSTNAMELEN];
+
+                       if (*p == '\0')
+                       {
+                               printf("Usage: /canon address\n");
+                               return;
+                       }
+                       strcpy(host, p);
+                       getcanonname(host, sizeof(host), HasWildcardMX, &rcode);
+                       printf("getcanonname(%s) returns %s (%d)\n",
+                               p, host, rcode);
+               }
+               else if (strcasecmp(&line[1], "map") == 0)
+               {
+                       auto int rcode = EX_OK;
+
+                       if (*p == '\0')
+                       {
+                               printf("Usage: /map mapname key\n");
+                               return;
+                       }
+                       for (q = p; *q != '\0' && !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;
+                       }
+                       printf("map_lookup: %s (%s) ", p, q);
+                       p = (*map->s_map.map_class->map_lookup)
+                                       (&map->s_map, q, NULL, &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);
+               }
+               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);
+               }
+               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;
+                       int rs = strtorwset(p, NULL, ST_FIND);
+
+                       if (rs < 0)
+                               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);
+}
index 7c13db9..df202e9 100644 (file)
@@ -32,7 +32,7 @@
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 #
-#       @(#)makesendmail       8.5 (Berkeley) 2/27/94
+#       @(#)makesendmail       8.29 (Berkeley) 8/1/95
 #
 
 #
 #  multiple architectures and Makefiles.
 #
 
-# determine machine architecture
-arch=`uname -m`
+if [ "x${1-""}" = "x-m" ]
+then
+       # show Makefile name only
+       mflag=1
+else
+       mflag=""
+fi
+
+#
+#  Do heuristic guesses !ONLY! for machines that do not have uname
+#
+if [ -d /LocalApps -a ! -f /bin/uname -a ! -f /usr/bin/uname ]
+then
+       # probably a NeXT box
+       arch=NeXT
+       os=Mach
+       rel=`strings /mach | grep 'Mach.*:' | sed -e 's/.* Mach //' -e 's/:.*//'`
+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
+fi
+
+if [ ! "$arch" -a ! "$os" -a ! "$rel" ]
+then
+       arch=`uname -m | sed -e 's/ //g'`
+       os=`uname -s | sed 's/\//-/g'`
+       rel=`uname -r`
+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;;
@@ -49,65 +106,177 @@ in
   9000/*)      arch=`echo $arch | sed -e 's/9000.//' -e 's/..$/xx/'`;;
 esac
 
-# determine operating system type
-os=`uname -s`
+# tweak operating system type and release
+case $os
+in
+  DYNIX-ptx)   os=PTX;;
+  Paragon*)    os=Paragon;;
+  HP-UX)       rel=`echo $rel | sed -e 's/^[^.]*\.0*//'`;;
+  AIX)         rel=`uname -v`;;
+  BSD-386)     os=BSD-OS;;
+esac
+
+# get "base part" of operating system release
+rbase=`echo $rel | sed -e 's/\..*//'`
 
-# determine operating system release
-rel=`uname -r`
-rbase=`echo $rel | sed 's/\..*//''`
+# 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, arch=$arch, sfx=$sfx"
 
 # now try to find a reasonable object directory
-if [ -r obj.$os.$arch.$rel ]; then
-       obj=obj.$os.$arch.$rel
-elif [ -r obj.$os.$arch.$rbase.x ]; then
-       obj=obj.$os.$arch.$rbase.x
-elif [ -r obj.$os.$rel ]; then
-       obj=obj.$os.$rel
-elif [ -r obj.$os.$rbase.x ]; then
-       obj=obj.$os.$rbase.x
-elif [ -r obj.$os.$arch ]; then
-       obj=obj.$os.$arch
-elif [ -r obj.$arch.$rel ]; then
-       obj=obj.$arch.$rel
-elif [ -r obj.$arch.$rbase.x ]; then
-       obj=obj.$arch.$rbase.x
-elif [ -r obj.$os ]; then
-       obj=obj.$os
-elif [ -r obj.$arch ]; then
-       obj=obj.$arch
-elif [ -r obj.$rel ]; then
-       obj=obj.$rel
+if [ -r obj.$os.$rel.$arch$sfx ]; then
+       obj=obj.$os.$rel.$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
 else
        # no existing obj directory -- try to create one if Makefile found
-       obj=obj.$os.$arch.$rel
-       if [ -r Makefile.$os.$arch.$rel ]; then
-               makefile=Makefile.$os.$arch.$rel
-       elif [ -r Makefile.$os.$arch.$rbase.x ]; then
-               makefile=Makefile.$os.$arch.$rbase.x
-       elif [ -r Makefile.$os.$rel ]; then
+       obj=obj.$os.$rel.$arch$sfx
+       if [ -r Makefiles/Makefile.$os.$rel.$arch$sfx ]; then
+               makefile=Makefile.$os.$rel.$arch$sfx
+       elif [ -r Makefiles/Makefile.$os.$rel.$arch ]; then
+               makefile=Makefile.$os.$rel.$arch
+       elif [ -r Makefiles/Makefile.$os.$rbase.x.$arch$sfx ]; then
+               makefile=Makefile.$os.$rbase.x.$arch$sfx
+       elif [ -r Makefiles/Makefile.$os.$rbase.x.$arch ]; then
+               makefile=Makefile.$os.$rbase.x.$arch
+       elif [ -r Makefiles/Makefile.$os.$rel$sfx ]; then
+               makefile=Makefile.$os.$rel$sfx
+       elif [ -r Makefiles/Makefile.$os.$rel ]; then
                makefile=Makefile.$os.$rel
-       elif [ -r Makefile.$os.$rbase.x ]; then
+       elif [ -r Makefiles/Makefile.$os.$rbase.x$sfx ]; then
+               makefile=Makefile.$os.$rbase.x$sfx
+       elif [ -r Makefiles/Makefile.$os.$rbase.x ]; then
                makefile=Makefile.$os.$rbase.x
-       elif [ -r Makefile.$os.$arch ]; then
+       elif [ -r Makefiles/Makefile.$os.$arch$sfx ]; then
+               makefile=Makefile.$os.$arch$sfx
+       elif [ -r Makefiles/Makefile.$os.$arch ]; then
                makefile=Makefile.$os.$arch
-       elif [ -r Makefile.$arch.$rel ]; then
-               makefile=Makefile.$arch.$rel
-       elif [ -r Makefile.$arch.$rbase.x ]; then
-               makefile=Makefile.$arch.$rbase.x
-       elif [ -r Makefile.$os ]; then
+       elif [ -r Makefiles/Makefile.$rel.$arch$sfx ]; then
+               makefile=Makefile.$rel.$arch$sfx
+       elif [ -r Makefiles/Makefile.$rel.$arch ]; then
+               makefile=Makefile.$rel.$arch
+       elif [ -r Makefiles/Makefile.$rbase.x.$arch$sfx ]; then
+               makefile=Makefile.$rbase.x.$arch$sfx
+       elif [ -r Makefiles/Makefile.$rbase.x.$arch ]; then
+               makefile=Makefile.$rbase.x.$arch
+       elif [ -r Makefiles/Makefile.$os$sfx ]; then
+               makefile=Makefile.$os$sfx
+       elif [ -r Makefiles/Makefile.$os ]; then
                makefile=Makefile.$os
-       elif [ -r Makefile.$arch ]; then
+       elif [ -r Makefiles/Makefile.$arch$sfx ]; then
+               makefile=Makefile.$arch$sfx
+       elif [ -r Makefiles/Makefile.$arch ]; then
                makefile=Makefile.$arch
-       elif [ -r Makefile.$rel ]; then
+       elif [ -r Makefiles/Makefile.$rel$sfx ]; then
+               makefile=Makefile.$rel$sfx
+       elif [ -r Makefiles/Makefile.$rel ]; then
                makefile=Makefile.$rel
+       elif [ -r Makefiles/Makefile.$rel$sfx ]; then
+               makefile=Makefile.$rel$sfx
        else
                echo "Cannot determine how to support $arch.$os.$rel"
                exit 1
        fi
+       if [ "$mflag" ]
+       then
+               echo "Will run in virgin $obj using $makefile"
+               exit 0
+       fi
        echo "Creating $obj using $makefile"
        mkdir $obj
-       (cd $obj; ln -s ../*.[ch158] ../sendmail.hf .; ln -s ../$makefile Makefile)
+       (cd $obj; ln -s ../*.[ch158] ../sendmail.hf .; ln -s ../Makefiles/$makefile Makefile)
+       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"
+               exit 1
+       fi
+       case $makefile
+       in
+         ../Makefiles/*)
+               makefile=`echo $makefile | sed 's/...Makefiles.//'`
+               echo "Will run in existing $obj using $makefile"
+               ;;
+
+         *)
+               echo "Will run in existing $obj using custom $makefile"
+               ;;
+       esac
+       exit 0
+fi
+
 echo "Making in $obj"
 cd $obj
-exec make -f Makefile $*
+if [ $# = 0 ]
+then
+       exec ${MAKE-make}
+else
+       exec ${MAKE-make} "$@"
+fi
index a2b3337..23060d0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1992 Eric P. Allman.
+ * Copyright (c) 1992, 1995 Eric P. Allman.
  * Copyright (c) 1992, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)map.c      8.25.1.1 (Berkeley) 2/10/95";
+static char sccsid[] = "@(#)map.c      8.108 (Berkeley) 11/29/95";
 #endif /* not lint */
 
 #include "sendmail.h"
 
 #ifdef NDBM
-#include <ndbm.h>
+# include <ndbm.h>
 #endif
 #ifdef NEWDB
-#include <db.h>
+# ifdef R_FIRST
+  ERROR README:        You are running the Berkeley DB version of ndbm.h.  See
+  ERROR README:        the READ_ME file about tweaking Berkeley DB so it can
+  ERROR README:        coexist with NDBM, or delete -DNDBM from the Makefile.
+# endif
+# include <db.h>
 #endif
 #ifdef NIS
-#include <rpcsvc/ypclnt.h>
+  struct dom_binding;  /* forward reference needed on IRIX */
+# include <rpcsvc/ypclnt.h>
 #endif
 
 /*
@@ -82,11 +88,24 @@ static char sccsid[] = "@(#)map.c   8.25.1.1 (Berkeley) 2/10/95";
 **
 **     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
 
+#define EX_NOTFOUND    EX_NOHOST
+
 extern bool    aliaswait __P((MAP *, char *, int));
+extern bool    extract_canonname __P((char *, char *, char[]));
+
+#if defined(O_EXLOCK) && HASFLOCK
+# 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
 \f/*
 **  MAP_PARSEARGS -- parse config line arguments for database lookup
 **
@@ -141,9 +160,59 @@ map_parseargs(map, ap)
                        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 '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;
+#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++;
@@ -152,6 +221,10 @@ map_parseargs(map, ap)
        }
        if (map->map_app != NULL)
                map->map_app = newstr(map->map_app);
+       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')
        {
@@ -168,7 +241,8 @@ map_parseargs(map, ap)
        if (*p != '\0')
                map->map_rebuild = newstr(p);
 
-       if (map->map_file == NULL)
+       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);
@@ -316,13 +390,14 @@ map_rewrite(map, s, slen, av)
 **             if ~NDBM: reads the aliases into the symbol table.
 */
 
+void
 initmaps(rebuild, e)
        bool rebuild;
        register ENVELOPE *e;
 {
        extern void map_init();
 
-#ifdef XDEBUG
+#if XDEBUG
        checkfd012("entering initmaps");
 #endif
        CurEnv = e;
@@ -335,7 +410,7 @@ initmaps(rebuild, e)
        {
                stabapply(map_init, 0);
        }
-#ifdef XDEBUG
+#if XDEBUG
        checkfd012("exiting initmaps");
 #endif
 }
@@ -356,9 +431,10 @@ map_init(s, rebuild)
                return;
 
        if (tTd(38, 2))
-               printf("map_init(%s:%s, %d)\n",
+               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,
                        rebuild);
 
@@ -386,21 +462,239 @@ map_init(s, rebuild)
                if (map->map_class->map_open(map, O_RDONLY))
                {
                        if (tTd(38, 4))
-                               printf("\t%s:%s: valid\n",
+                               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: invalid: %s\n",
-                               map->map_class->map_cname == NULL ? "NULL" :
-                                       map->map_class->map_cname,
-                               map->map_file == NULL ? "NULL" :
-                                       map->map_file,
-                               errstring(errno));
+               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;
+                       }
+               }
+       }
+}
+\f/*
+**  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 (stat == EX_NOHOST && !got_tempfail)
+               h_errno = HOST_NOT_FOUND;
+       else
+               h_errno = TRY_AGAIN;
+#endif
+
+       return FALSE;
+}
+\f/*
+**  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.
+**
+**     Returns:
+**             TRUE -- if the line matched the desired name.
+**             FALSE -- otherwise.
+*/
+
+bool
+extract_canonname(name, line, cbuf)
+       char *name;
+       char *line;
+       char cbuf[];
+{
+       int i;
+       char *p;
+       bool found = FALSE;
+       extern char *get_column();
+
+       cbuf[0] = '\0';
+       if (line[0] == '#')
+               return FALSE;
+
+       for (i = 1; ; i++)
+       {
+               char nbuf[MAXNAME + 1];
+
+               p = get_column(line, i, '\0', nbuf);
+               if (p == NULL)
+                       break;
+               if (cbuf[0] == '\0' ||
+                   (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL))
+                       strcpy(cbuf, 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 < MAXNAME)
+               {
+                       p = &cbuf[strlen(cbuf)];
+                       *p++ = '.';
+                       strcpy(p, domain);
+               }
        }
+       return found;
 }
 \f/*
 **  NDBM modules
@@ -419,40 +713,99 @@ ndbm_map_open(map, mode)
 {
        register DBM *dbm;
        struct stat st;
+       int fd;
 
        if (tTd(38, 2))
-               printf("ndbm_map_open(%s, %d)\n", map->map_file, mode);
+               printf("ndbm_map_open(%s, %s, %d)\n",
+                       map->map_mname, map->map_file, mode);
 
+#if LOCK_ON_OPEN
+       if (mode == O_RDONLY)
+               mode |= O_SHLOCK;
+       else
+               mode |= O_CREAT|O_TRUNC|O_EXLOCK;
+#else
        if (mode == O_RDWR)
+       {
+# ifdef NOFTRUNCATE
+               /*
+               **  Warning: race condition.  Try to lock the file as
+               **  quickly as possible after opening it.
+               */
+
                mode |= O_CREAT|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;
+               char dirfile[MAXNAME + 1];
+               char pagfile[MAXNAME + 1];
+
+               sprintf(dirfile, "%s.dir", map->map_file);
+               sprintf(pagfile, "%s.pag", map->map_file);
+               dirfd = open(dirfile, mode|O_CREAT, DBMMODE);
+               pagfd = open(pagfile, mode|O_CREAT, DBMMODE);
+
+               if (dirfd < 0 || pagfd < 0)
+               {
+                       syserr("ndbm_map_open: cannot create database %s",
+                               map->map_file);
+                       close(dirfd);
+                       close(pagfd);
+                       return FALSE;
+               }
+               if (!lockfile(dirfd, map->map_file, ".dir", LOCK_EX))
+                       syserr("ndbm_map_open: cannot lock %s.dir",
+                               map->map_file);
+               if (ftruncate(dirfd, 0) < 0)
+                       syserr("ndbm_map_open: cannot truncate %s.dir",
+                               map->map_file);
+               if (ftruncate(pagfd, 0) < 0)
+                       syserr("ndbm_map_open: cannot truncate %s.pag",
+                               map->map_file);
+
+               /* we can safely unlock because others will wait for @:@ */
+               close(dirfd);
+               close(pagfd);
+# endif
+       }
+#endif
 
        /* open the database */
        dbm = dbm_open(map->map_file, mode, DBMMODE);
        if (dbm == NULL)
        {
-#ifdef MAYBENEXTRELEASE
-               if (aliaswait(map, ".pag", FALSE))
+               if (bitset(MF_ALIAS, map->map_mflags) &&
+                   aliaswait(map, ".pag", FALSE))
                        return TRUE;
-#endif
                if (!bitset(MF_OPTIONAL, map->map_mflags))
                        syserr("Cannot open DBM database %s", map->map_file);
                return FALSE;
        }
        map->map_db1 = (void *) dbm;
+       fd = dbm_dirfno((DBM *) map->map_db1);
        if (mode == O_RDONLY)
        {
+#if LOCK_ON_OPEN
+               if (fd >= 0)
+                       (void) lockfile(fd, map->map_file, ".pag", LOCK_UN);
+#endif
                if (bitset(MF_ALIAS, map->map_mflags) &&
                    !aliaswait(map, ".pag", TRUE))
                        return FALSE;
        }
        else
        {
-               int fd;
-
                /* exclusive lock for duration of rebuild */
-               fd = dbm_dirfno((DBM *) map->map_db1);
+#if !LOCK_ON_OPEN
                if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags) &&
                    lockfile(fd, map->map_file, ".dir", LOCK_EX))
+#endif
                        map->map_mflags |= MF_LOCKED;
        }
        if (fstat(dbm_dirfno((DBM *) map->map_db1), &st) >= 0)
@@ -477,7 +830,8 @@ ndbm_map_lookup(map, name, av, statp)
        char keybuf[MAXNAME + 1];
 
        if (tTd(38, 20))
-               printf("ndbm_map_lookup(%s)\n", name);
+               printf("ndbm_map_lookup(%s, %s)\n",
+                       map->map_mname, name);
 
        key.dptr = name;
        key.dsize = strlen(name);
@@ -532,7 +886,8 @@ ndbm_map_store(map, lhs, rhs)
        int stat;
 
        if (tTd(38, 12))
-               printf("ndbm_map_store(%s, %s)\n", lhs, rhs);
+               printf("ndbm_map_store(%s, %s, %s)\n",
+                       map->map_mname, lhs, rhs);
 
        key.dsize = strlen(lhs);
        key.dptr = lhs;
@@ -549,7 +904,33 @@ ndbm_map_store(map, lhs, rhs)
        stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT);
        if (stat > 0)
        {
-               usrerr("050 Warning: duplicate alias name %s", lhs);
+               if (!bitset(MF_APPEND, map->map_mflags))
+                       usrerr("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, NULL, &xstat);
+                       if (old.dptr != NULL && *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);
+                               }
+                               sprintf(buf, "%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)
@@ -566,7 +947,8 @@ ndbm_map_close(map)
        register MAP  *map;
 {
        if (tTd(38, 9))
-               printf("ndbm_map_close(%s, %x)\n", map->map_file, map->map_mflags);
+               printf("ndbm_map_close(%s, %s, %x)\n",
+                       map->map_mname, map->map_file, map->map_mflags);
 
        if (bitset(MF_WRITABLE, map->map_mflags))
        {
@@ -577,11 +959,14 @@ ndbm_map_close(map)
                inclnull = bitset(MF_INCLNULL, map->map_mflags);
                map->map_mflags &= ~MF_INCLNULL;
 
-               (void) sprintf(buf, "%010ld", curtime());
-               ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
+               if (strstr(map->map_file, "/yp/") != NULL)
+               {
+                       (void) sprintf(buf, "%010ld", curtime());
+                       ndbm_map_store(map, "YP_LAST_MODIFIED", buf);
 
-               (void) gethostname(buf, sizeof buf);
-               ndbm_map_store(map, "YP_MASTER_NAME", buf);
+                       (void) gethostname(buf, sizeof buf);
+                       ndbm_map_store(map, "YP_MASTER_NAME", buf);
+               }
 
                if (inclnull)
                        map->map_mflags |= MF_INCLNULL;
@@ -616,60 +1001,112 @@ bool
 bt_map_open(map, mode)
        MAP *map;
        int mode;
+{
+       if (tTd(38, 2))
+               printf("bt_map_open(%s, %s, %d)\n",
+                       map->map_mname, map->map_file, mode);
+       return db_map_open(map, mode, DB_BTREE);
+}
+
+bool
+hash_map_open(map, mode)
+       MAP *map;
+       int mode;
+{
+       if (tTd(38, 2))
+               printf("hash_map_open(%s, %s, %d)\n",
+                       map->map_mname, map->map_file, mode);
+       return db_map_open(map, mode, DB_HASH);
+}
+
+bool
+db_map_open(map, mode, dbtype)
+       MAP *map;
+       int mode;
+       DBTYPE dbtype;
 {
        DB *db;
        int i;
        int omode;
        int fd;
+       int saveerrno;
        struct stat st;
-       char buf[MAXNAME];
+       char buf[MAXNAME + 1];
 
-       if (tTd(38, 2))
-               printf("bt_map_open(%s, %d)\n", map->map_file, mode);
+       (void) strcpy(buf, map->map_file);
+       i = strlen(buf);
+       if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
+               (void) strcat(buf, ".db");
 
        omode = mode;
-       if (omode == O_RDWR)
-       {
-               omode |= O_CREAT|O_TRUNC;
-#if defined(O_EXLOCK) && HASFLOCK
-               omode |= O_EXLOCK;
+
+#if LOCK_ON_OPEN
+       if (mode == O_RDWR)
+               omode |= O_CREAT|O_TRUNC|O_EXLOCK;
 # if !OLD_NEWDB
-       }
        else
-       {
                omode |= O_SHLOCK;
 # endif
-#endif
+#else
+       if (mode == O_RDWR)
+               omode |= O_CREAT;
+
+       /*
+       **  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)
+       {
+               syserr("db_map_open: cannot pre-open database %s",
+                       buf);
+               close(fd);
+               return FALSE;
        }
+       if (!lockfile(fd, map->map_file, ".db",
+                     mode == O_RDONLY ? LOCK_SH : LOCK_EX))
+               syserr("db_map_open: cannot lock %s", buf);
+       if (mode == O_RDWR)
+               omode |= O_TRUNC;
+#endif
+
+       db = dbopen(buf, omode, DBMMODE, dbtype, NULL);
+       saveerrno = errno;
+
+#if !LOCK_ON_OPEN
+       /* we can safely unlock now because others will wait for @:@ */
+       close(fd);
+#endif
 
-       (void) strcpy(buf, map->map_file);
-       i = strlen(buf);
-       if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
-               (void) strcat(buf, ".db");
-       db = dbopen(buf, omode, DBMMODE, DB_BTREE, NULL);
        if (db == NULL)
        {
-#ifdef MAYBENEXTRELEASE
-               if (aliaswait(map, ".db", FALSE))
+               if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
+                   aliaswait(map, ".db", FALSE))
                        return TRUE;
-#endif
+               errno = saveerrno;
                if (!bitset(MF_OPTIONAL, map->map_mflags))
-                       syserr("Cannot open BTREE database %s", map->map_file);
+                       syserr("Cannot open DB database %s", map->map_file);
                return FALSE;
        }
-#if !OLD_NEWDB && HASFLOCK
+#if !OLD_NEWDB
        fd = db->fd(db);
-# if !defined(O_EXLOCK)
+# if LOCK_ON_OPEN
+       if (fd >= 0)
+       {
+               if (mode == O_RDONLY)
+                       (void) lockfile(fd, map->map_file, ".db", LOCK_UN);
+               else
+                       map->map_mflags |= MF_LOCKED;
+       }
+# else
        if (mode == O_RDWR && fd >= 0)
        {
                if (lockfile(fd, map->map_file, ".db", LOCK_EX))
                        map->map_mflags |= MF_LOCKED;
        }
-# else
-       if (mode == O_RDONLY && fd >= 0)
-               (void) lockfile(fd, map->map_file, ".db", LOCK_UN);
-       else
-               map->map_mflags |= MF_LOCKED;
 # endif
 #endif
 
@@ -685,138 +1122,55 @@ bt_map_open(map, mode)
 #endif
 
        map->map_db2 = (void *) db;
-       if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
-               if (!aliaswait(map, ".db", TRUE))
-                       return FALSE;
+       if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) &&
+           !aliaswait(map, ".db", TRUE))
+               return FALSE;
        return TRUE;
 }
 
 
 /*
-**  HASH_MAP_INIT -- HASH-style map initialization
+**  DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
 */
 
-bool
-hash_map_open(map, mode)
+char *
+db_map_lookup(map, name, av, statp)
        MAP *map;
-       int mode;
+       char *name;
+       char **av;
+       int *statp;
 {
-       DB *db;
-       int i;
-       int omode;
+       DBT key, val;
+       register DB *db = (DB *) map->map_db2;
+       int st;
+       int saveerrno;
        int fd;
-       struct stat st;
-       char buf[MAXNAME];
+       char keybuf[MAXNAME + 1];
 
-       if (tTd(38, 2))
-               printf("hash_map_open(%s, %d)\n", map->map_file, mode);
+       if (tTd(38, 20))
+               printf("db_map_lookup(%s, %s)\n",
+                       map->map_mname, name);
 
-       omode = mode;
-       if (omode == O_RDWR)
+       key.size = strlen(name);
+       if (key.size > sizeof keybuf - 1)
+               key.size = sizeof keybuf - 1;
+       key.data = keybuf;
+       bcopy(name, keybuf, key.size + 1);
+       if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+               makelower(keybuf);
+#if !OLD_NEWDB
+       fd = db->fd(db);
+       if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
+               (void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
+#endif
+       st = 1;
+       if (bitset(MF_TRY0NULL, map->map_mflags))
        {
-               omode |= O_CREAT|O_TRUNC;
-#if defined(O_EXLOCK) && HASFLOCK
-               omode |= O_EXLOCK;
-# if !OLD_NEWDB
+               st = db->get(db, &key, &val, 0);
+               if (st == 0)
+                       map->map_mflags &= ~MF_TRY1NULL;
        }
-       else
-       {
-               omode |= O_SHLOCK;
-# endif
-#endif
-       }
-
-       (void) strcpy(buf, map->map_file);
-       i = strlen(buf);
-       if (i < 3 || strcmp(&buf[i - 3], ".db") != 0)
-               (void) strcat(buf, ".db");
-       db = dbopen(buf, omode, DBMMODE, DB_HASH, NULL);
-       if (db == NULL)
-       {
-#ifdef MAYBENEXTRELEASE
-               if (aliaswait(map, ".db", FALSE))
-                       return TRUE;
-#endif
-               if (!bitset(MF_OPTIONAL, map->map_mflags))
-                       syserr("Cannot open HASH database %s", map->map_file);
-               return FALSE;
-       }
-#if !OLD_NEWDB && HASFLOCK
-       fd = db->fd(db);
-# if !defined(O_EXLOCK)
-       if (mode == O_RDWR && fd >= 0)
-       {
-               if (lockfile(fd, map->map_file, ".db", LOCK_EX))
-                       map->map_mflags |= MF_LOCKED;
-       }
-# else
-       if (mode == O_RDONLY && fd >= 0)
-               (void) lockfile(fd, map->map_file, ".db", LOCK_UN);
-       else
-               map->map_mflags |= MF_LOCKED;
-# endif
-#endif
-
-       /* try to make sure that at least the database header is on disk */
-       if (mode == O_RDWR)
-#if OLD_NEWDB
-               (void) db->sync(db);
-#else
-               (void) db->sync(db, 0);
-
-       if (fd >= 0 && fstat(fd, &st) >= 0)
-               map->map_mtime = st.st_mtime;
-#endif
-
-       map->map_db2 = (void *) db;
-       if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags))
-               if (!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 st;
-       int saveerrno;
-       int fd;
-       char keybuf[MAXNAME + 1];
-
-       if (tTd(38, 20))
-               printf("db_map_lookup(%s)\n", name);
-
-       key.size = strlen(name);
-       if (key.size > sizeof keybuf - 1)
-               key.size = sizeof keybuf - 1;
-       key.data = keybuf;
-       bcopy(name, keybuf, key.size + 1);
-       if (!bitset(MF_NOFOLDCASE, map->map_mflags))
-               makelower(keybuf);
-#if !OLD_NEWDB
-       fd = db->fd(db);
-       if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags))
-               (void) lockfile(db->fd(db), map->map_file, ".db", LOCK_SH);
-#endif
-       st = 1;
-       if (bitset(MF_TRY0NULL, map->map_mflags))
-       {
-               st = db->get(db, &key, &val, 0);
-               if (st == 0)
-                       map->map_mflags &= ~MF_TRY1NULL;
-       }
-       if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
+       if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags))
        {
                key.size++;
                st = db->get(db, &key, &val, 0);
@@ -857,8 +1211,9 @@ db_map_store(map, lhs, rhs)
        DBT data;
        register DB *db = map->map_db2;
 
-       if (tTd(38, 20))
-               printf("db_map_store(%s, %s)\n", lhs, rhs);
+       if (tTd(38, 12))
+               printf("db_map_store(%s, %s, %s)\n",
+                       map->map_mname, lhs, rhs);
 
        key.size = strlen(lhs);
        key.data = lhs;
@@ -875,7 +1230,32 @@ db_map_store(map, lhs, rhs)
        stat = db->put(db, &key, &data, R_NOOVERWRITE);
        if (stat > 0)
        {
-               usrerr("050 Warning: duplicate alias name %s", lhs);
+               if (!bitset(MF_APPEND, map->map_mflags))
+                       usrerr("050 Warning: duplicate alias name %s", lhs);
+               else
+               {
+                       static char *buf = NULL;
+                       static int bufsiz = 0;
+                       DBT old;
+
+                       old.data = db_map_lookup(map, key.data, 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);
+                               }
+                               sprintf(buf, "%s,%s", data.data, old.data);
+                               data.size = data.size + old.size + 1;
+                               data.data = buf;
+                               if (tTd(38, 9))
+                                       printf("db_map_store append=%s\n", data.data);
+                       }
+               }
                stat = db->put(db, &key, &data, 0);
        }
        if (stat != 0)
@@ -894,7 +1274,8 @@ db_map_close(map)
        register DB *db = map->map_db2;
 
        if (tTd(38, 9))
-               printf("db_map_close(%s, %x)\n", map->map_file, map->map_mflags);
+               printf("db_map_close(%s, %s, %x)\n",
+                       map->map_mname, map->map_file, map->map_mflags);
 
        if (bitset(MF_WRITABLE, map->map_mflags))
        {
@@ -930,10 +1311,10 @@ nis_map_open(map, mode)
        register char *p;
        auto char *vp;
        auto int vsize;
-       char *master;
 
        if (tTd(38, 2))
-               printf("nis_map_open(%s)\n", map->map_file);
+               printf("nis_map_open(%s, %s, %d)\n",
+                       map->map_mname, map->map_file, mode);
 
        if (mode != O_RDONLY)
        {
@@ -967,7 +1348,7 @@ nis_map_open(map, mode)
                if (yperr != 0)
                {
                        if (!bitset(MF_OPTIONAL, map->map_mflags))
-                               syserr("NIS map %s specified, but NIS not running\n",
+                               syserr("421 NIS map %s specified, but NIS not running\n",
                                        map->map_file);
                        return FALSE;
                }
@@ -977,14 +1358,29 @@ nis_map_open(map, mode)
        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",
+               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)
-               return TRUE;
+       {
+               /*
+               **  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("Cannot bind to domain %s: %s", map->map_domain,
-                       yperr_string(yperr));
+       {
+               syserr("421 Cannot bind to map %s in domain %s: %s",
+                       map->map_file, map->map_domain, yperr_string(yperr));
+       }
 
        return FALSE;
 }
@@ -1008,7 +1404,8 @@ nis_map_lookup(map, name, av, statp)
        char keybuf[MAXNAME + 1];
 
        if (tTd(38, 20))
-               printf("nis_map_lookup(%s)\n", name);
+               printf("nis_map_lookup(%s, %s)\n",
+                       map->map_mname, name);
 
        buflen = strlen(name);
        if (buflen > sizeof keybuf - 1)
@@ -1046,95 +1443,124 @@ nis_map_lookup(map, name, av, statp)
 
 
 /*
-**  NIS_MAP_STORE
-*/
-
-void
-nis_map_store(map, lhs, rhs)
-       MAP *map;
-       char *lhs;
-       char *rhs;
-{
-       /* nothing */
-}
-
-
-/*
-**  NIS_MAP_CLOSE
-*/
-
-void
-nis_map_close(map)
-       MAP *map;
-{
-       /* nothing */
-}
-
-#endif /* NIS */
-\f/*
-**  STAB (Symbol Table) Modules
-*/
-
-
-/*
-**  STAB_MAP_LOOKUP -- look up alias in symbol table
+**  NIS_GETCANONNAME -- look up canonical name in NIS
 */
 
-char *
-stab_map_lookup(map, name, av, pstat)
-       register MAP *map;
+bool
+nis_getcanonname(name, hbsize, statp)
        char *name;
-       char **av;
-       int *pstat;
+       int hbsize;
+       int *statp;
 {
-       register STAB *s;
+       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("stab_lookup(%s)\n", name);
+               printf("nis_getcanonname(%s)\n", name);
 
-       s = stab(name, ST_ALIAS, ST_FIND);
-       if (s != NULL)
-               return (s->s_alias);
-       return (NULL);
-}
+       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;
+       }
+       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))
+       {
+               /* 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;
+}
 
-/*
-**  STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
+#endif
+\f/*
+**  NISPLUS Modules
+**
+**     This code donated by Sun Microsystems.
 */
 
-void
-stab_map_store(map, lhs, rhs)
-       register MAP *map;
-       char *lhs;
-       char *rhs;
-{
-       register STAB *s;
+#ifdef NISPLUS
 
-       s = stab(lhs, ST_ALIAS, ST_ENTER);
-       s->s_alias = newstr(rhs);
-}
+#undef NIS /* symbol conflict in nis.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] != '.')
 
 /*
-**  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.
+**  NISPLUS_MAP_OPEN -- open nisplus table
 */
 
 bool
-stab_map_open(map, mode)
-       register MAP *map;
+nisplus_map_open(map, mode)
+       MAP *map;
        int mode;
 {
-       FILE *af;
-       struct stat st;
+       register char *p;
+       char qbuf[MAXLINE + NIS_MAXNAMELEN];
+       nis_result *res = NULL;
+       u_int objs_len;
+       nis_object *obj_ptr;
+       int retry_cnt, max_col, i;
 
        if (tTd(38, 2))
-               printf("stab_map_open(%s)\n", map->map_file);
+               printf("nisplus_map_open(%s, %s, %d)\n",
+                       map->map_mname, map->map_file, mode);
 
        if (mode != O_RDONLY)
        {
@@ -1142,51 +1568,1111 @@ stab_map_open(map, mode)
                return FALSE;
        }
 
-       af = fopen(map->map_file, "r");
-       if (af == NULL)
-               return FALSE;
-       readaliases(map, af, TRUE);
+       if (*map->map_file == '\0')
+               map->map_file = "mail_aliases.org_dir";
 
-       if (fstat(fileno(af), &st) >= 0)
-               map->map_mtime = st.st_mtime;
-       fclose(af);
+       if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL)
+       {
+               /* set default NISPLUS Domain to $m */
+               extern char *nisplus_default_domain();
 
-       return TRUE;
-}
+               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("");
+
+       /* check to see if this map actually exists */
+       if (PARTIAL_NAME(map->map_file))
+               sprintf(qbuf, "%s.%s", map->map_file, map->map_domain);
+       else
+               strcpy(qbuf, map->map_file);
+       
+       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 = EBADR;
+                               return FALSE;
+                       }
+                       /* try not to overwhelm hosed server */
+                       sleep(2);
+                       break;
 
-/*
-**  STAB_MAP_CLOSE -- close symbol table.
-**
-**     Since this is in memory, there is nothing to do.
-*/
+                 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 = EBADR;
+                       return FALSE;
+               }
+       }
 
-void
-stab_map_close(map)
-       MAP *map;
-{
-       /* ignore it */
+       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 = EBADR;
+               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 = EBADR;
+               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 = EBADR;
+       return FALSE;
 }
-\f/*
-**  Implicit Modules
-**
-**     Tries several types.  For back compatibility of aliases.
-*/
 
 
 /*
-**  IMPL_MAP_LOOKUP -- lookup in best open database
+**  NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
 */
 
 char *
-impl_map_lookup(map, name, av, pstat)
+nisplus_map_lookup(map, name, av, statp)
        MAP *map;
        char *name;
        char **av;
-       int *pstat;
+       int *statp;
+{
+       char *vp;
+       auto int vsize;
+       int buflen;
+       char search_key[MAXNAME + 1];
+       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;
+               }
+       }
+               
+       buflen = strlen(name);
+       if (buflen > sizeof search_key - 1)
+               buflen = sizeof search_key - 1;
+       bcopy(name, search_key, buflen + 1);
+       if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+               makelower(search_key);
+
+       /* construct the query */
+       if (PARTIAL_NAME(map->map_file))
+               sprintf(qbuf, "[%s=%s],%s.%s", map->map_keycolnm,
+                       search_key, map->map_file, map->map_domain);
+       else
+               sprintf(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)
+                               syslog(LOG_WARNING,
+                                 "%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);
+               }
+
+               vp = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno));
+               /* set the length of the result */
+               if (vp == NULL)
+                       vp = "";
+               vsize = strlen(vp);
+               if (tTd(38, 20))
+                       printf("nisplus_map_lookup(%s), found %s\n",
+                               name, vp);
+               if (bitset(MF_MATCHONLY, map->map_mflags))
+                       str = map_rewrite(map, name, strlen(name), NULL);
+               else
+                       str = map_rewrite(map, vp, 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 */
+               sprintf(qbuf, "[name=%s],hosts.org_dir", nbuf);
+       }
+       else if (p[1] != '\0')
+       {
+               /* multi token -- take only first token in nbuf */
+               *p = '\0';
+               sprintf(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 *str;
+               char *domain;
+
+               if ((count = NIS_RES_NUMOBJ(result)) != 1)
+               {
+#ifdef LOG
+                       if (LogLevel > 10)
+                               syslog(LOG_WARNING,
+                                      "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
+                                      count);
+#endif
+
+                       /* ignore second entry */
+                       if (tTd(38, 20))
+                               printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n", name);
+               }
+
+               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
+                               sprintf(name, "%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();
+       strcpy(default_domain, p);
+       return default_domain;
+}
+
+#endif /* NISPLUS */
+\f/*
+**  HESIOD Modules
+*/
+
+#ifdef HESIOD
+
+#include <hesiod.h>
+
+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;
+       }
+
+       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;
+}
+
+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);
+               hp = hes_resolve(np, map->map_file);
+               if (np != nbuf)
+                       free(np);
+       }
+       else
+       {
+               hp = hes_resolve(name, map->map_file);
+       }
+       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;
+       }
+       
+       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
+\f/*
+**  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, 20))
+               printf("ni_map_open(%s, %s, %d)\n",
+                       map->map_mname, map->map_file, mode);
+
+       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 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", '\0');
+
+       if (vptr == NULL)
+       {
+               *statp = EX_NOHOST;
+               return FALSE;
+       }
+
+       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 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; ++i)
+       {
+               if (i == 0)
+               {
+                       nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
+               }
+               else
+               {
+                       if (lastni != NULL)
+                               ni_free(lastni);
+                       lastni = ni;
+                       nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
+               }
+
+               /*
+               **  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;
+
+               /*
+               **  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);
+
+       return propval;
+}
+
+#endif
+\f/*
+**  TEXT (unindexed text file) Modules
+**
+**     This code donated by Sun Microsystems.
+*/
+
+
+/*
+**  TEXT_MAP_OPEN -- open text table
+*/
+
+bool
+text_map_open(map, mode)
+       MAP *map;
+       int mode;
+{
+       struct stat sbuf;
+
+       if (tTd(38, 2))
+               printf("text_map_open(%s, %s, %d)\n",
+                       map->map_mname, map->map_file, mode);
+
+       if (mode != O_RDONLY)
+       {
+               errno = ENODEV;
+               return FALSE;
+       }
+
+       if (*map->map_file == '\0')
+       {
+               if (tTd(38, 2))
+                       printf("text_map_open(%s): file name required\n",
+                               map->map_mname);
+               return FALSE;
+       }
+
+       if (map->map_file[0] != '/')
+       {
+               if (tTd(38, 2))
+                       printf("text_map_open(%s, %s): file name must be fully qualified\n",
+                               map->map_mname, map->map_file);
+               return FALSE;
+       }
+       /* check to see if this map actually accessable */
+       if (access(map->map_file, R_OK) <0)
+               return FALSE;
+
+       /* check to see if this map actually exist */
+       if (stat(map->map_file, &sbuf) <0)
+       {
+               if (tTd(38, 2))
+                       printf("text_map_open(%s, %s): cannot stat\n",
+                               map->map_mname, map->map_file);
+               return FALSE;
+       }
+
+       if (!S_ISREG(sbuf.st_mode))
+       {
+               if (tTd(38, 2))
+                       printf("text_map_open(%s): %s is not a regular file\n",
+                               map->map_mname, map->map_file);
+               return FALSE;
+       }
+
+       if (map->map_keycolnm == NULL)
+               map->map_keycolno = 0;
+       else
+       {
+               if (!isdigit(*map->map_keycolnm))
+               {
+                       if (tTd(38, 2))
+                               printf("text_map_open(%s, %s): -k should specify a number, not %s\n",
+                                       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 (!isdigit(*map->map_valcolnm))
+               {
+                       if (tTd(38, 2))
+                               printf("text_map_open(%s, %s): -v should specify a number, not %s\n",
+                                       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);
+       }
+
+       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;
+       char search_key[MAXNAME + 1];
+       char linebuf[MAXLINE];
+       FILE *f;
+       char buf[MAXNAME + 1];
+       char delim;
+       int key_idx;
+       bool found_it;
+       extern char *get_column();
+
+
+       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 + 1);
+       if (!bitset(MF_NOFOLDCASE, map->map_mflags))
+               makelower(search_key);
+
+       f = fopen(map->map_file, "r");
+       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);
+               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);
+       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;
+{
+       int key_idx;
+       bool found;
+       FILE *f;
+       char linebuf[MAXLINE];
+       char cbuf[MAXNAME + 1];
+       char fbuf[MAXNAME + 1];
+       char nbuf[MAXNAME + 1];
+       extern char *get_column();
+
+       if (tTd(38, 20))
+               printf("text_getcanonname(%s)\n", name);
+
+       if (strlen(name) >= 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);
+       }
+       fclose(f);
+       if (!found)
+       {
+               *statp = EX_NOHOST;
+               return FALSE;
+       }
+
+       if (hbsize >= strlen(cbuf))
+       {
+               strcpy(name, cbuf);
+               *statp = EX_OK;
+               return TRUE;
+       }
+       *statp = EX_UNAVAILABLE;
+       return FALSE;
+}
+\f/*
+**  STAB (Symbol Table) Modules
+*/
+
+
+/*
+**  STAB_MAP_LOOKUP -- look up alias in symbol table
+*/
+
+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;
+       struct stat st;
+
+       if (tTd(38, 2))
+               printf("stab_map_open(%s, %s, %d)\n",
+                       map->map_mname, map->map_file, mode);
+
+       if (mode != O_RDONLY)
+       {
+               errno = ENODEV;
+               return FALSE;
+       }
+
+       af = fopen(map->map_file, "r");
+       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;
+}
+\f/*
+**  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)\n", name);
+               printf("impl_map_lookup(%s, %s)\n",
+                       map->map_mname, name);
 
 #ifdef NEWDB
        if (bitset(MF_IMPL_HASH, map->map_mflags))
@@ -1209,6 +2695,9 @@ impl_map_store(map, lhs, rhs)
        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);
@@ -1228,26 +2717,17 @@ bool
 impl_map_open(map, mode)
        MAP *map;
        int mode;
-{
-       struct stat stb;
-
-       if (tTd(38, 2))
-               printf("impl_map_open(%s, %d)\n", map->map_file, mode);
-
-       if (stat(map->map_file, &stb) < 0)
-       {
-               /* no alias file at all */
-               if (tTd(38, 3))
-                       printf("no map file\n");
-               return FALSE;
-       }
+{
+       if (tTd(38, 2))
+               printf("impl_map_open(%s, %s, %d)\n",
+                       map->map_mname, map->map_file, mode);
 
 #ifdef NEWDB
        map->map_mflags |= MF_IMPL_HASH;
        if (hash_map_open(map, mode))
        {
 #if defined(NDBM) && defined(NIS)
-               if (mode == O_RDONLY || access("/var/yp/Makefile", R_OK) != 0)
+               if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL)
 #endif
                        return TRUE;
        }
@@ -1284,6 +2764,9 @@ void
 impl_map_close(map)
        MAP *map;
 {
+       if (tTd(38, 9))
+               printf("impl_map_close(%s, %s, %x)\n",
+                       map->map_mname, map->map_file, map->map_mflags);
 #ifdef NEWDB
        if (bitset(MF_IMPL_HASH, map->map_mflags))
        {
@@ -1301,6 +2784,490 @@ impl_map_close(map)
 #endif
 }
 \f/*
+**  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);
+
+       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.
+*/
+
+char *
+user_map_lookup(map, key, av, statp)
+       MAP *map;
+       char *key;
+       char **av;
+       int *statp;
+{
+       struct passwd *pw;
+
+       if (tTd(38, 20))
+               printf("user_map_lookup(%s, %s)\n",
+                       map->map_mname, key);
+
+       pw = sm_getpwnam(key);
+       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:
+                       sprintf(buf, "%d", pw->pw_uid);
+                       rwval = buf;
+                       break;
+
+                 case 4:
+                       sprintf(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);
+       }
+}
+\f/*
+**  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)
+       {
+               strcpy(buf, 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");
+       }
+       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 && tTd(38, 20))
+       {
+               printf("prog_map_lookup(%s): empty answer\n",
+                       map->map_mname);
+               rval = NULL;
+       }
+       if (i > 0)
+       {
+               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);
+
+       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;
+}
+\f/*
+**  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);
+
+       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) sprintf(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);
+}
+\f/*
 **  NULL stubs
 */
 
@@ -1319,6 +3286,17 @@ null_map_close(map)
        return;
 }
 
+char *
+null_map_lookup(map, key, args, pstat)
+       MAP *map;
+       char *key;
+       char **args;
+       int *pstat;
+{
+       *pstat = EX_NOTFOUND;
+       return NULL;
+}
+
 void
 null_map_store(map, key, val)
        MAP *map;
@@ -1327,3 +3305,26 @@ null_map_store(map, key, 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,
+};
index 8211a62..ee0da2d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)mci.c      8.14 (Berkeley) 5/15/94";
+static char sccsid[] = "@(#)mci.c      8.22 (Berkeley) 11/18/95";
 #endif /* not lint */
 
 #include "sendmail.h"
@@ -65,6 +65,8 @@ static char sccsid[] = "@(#)mci.c     8.14 (Berkeley) 5/15/94";
 */
 
 MCI    **MciCache;             /* the open connection cache */
+
+extern void    mci_uncache __P((MCI **, bool));
 \f/*
 **  MCI_CACHE -- enter a connection structure into the open connection cache
 **
@@ -77,6 +79,7 @@ MCI   **MciCache;             /* the open connection cache */
 **             none.
 */
 
+void
 mci_cache(mci)
        register MCI *mci;
 {
@@ -108,7 +111,7 @@ mci_cache(mci)
                        mci, mci->mci_host, mcislot - MciCache);
 #ifdef LOG
        if (tTd(91, 100))
-               syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%s) in slot %d",
+               syslog(LOG_DEBUG, "%s: mci_cache: caching %x (%.100s) in slot %d",
                        CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
                        mci, mci->mci_host, mcislot - MciCache);
 #endif
@@ -188,6 +191,7 @@ mci_scan(savemci)
 **             none.
 */
 
+void
 mci_uncache(mcislot, doquit)
        register MCI **mcislot;
        bool doquit;
@@ -205,11 +209,12 @@ mci_uncache(mcislot, doquit)
                        mci, mci->mci_host, mcislot - MciCache, doquit);
 #ifdef LOG
        if (tTd(91, 100))
-               syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%s) from slot %d (%d)",
+               syslog(LOG_DEBUG, "%s: mci_uncache: uncaching %x (%.100s) from slot %d (%d)",
                        CurEnv->e_id ? CurEnv->e_id : "NOQUEUE",
                        mci, mci->mci_host, mcislot - MciCache, doquit);
 #endif
 
+#ifdef SMTP
        if (doquit)
        {
                message("Closing connection to %s", mci->mci_host);
@@ -224,6 +229,7 @@ mci_uncache(mcislot, doquit)
 #endif
        }
        else
+#endif
        {
                if (mci->mci_in != NULL)
                        xfclose(mci->mci_in, "mci_uncache", "mci_in");
@@ -248,6 +254,7 @@ mci_uncache(mcislot, doquit)
 **             none.
 */
 
+void
 mci_flush(doquit, allbut)
        bool doquit;
        MCI *allbut;
@@ -297,6 +304,7 @@ mci_get(host, m)
                        mci->mci_exitstat, mci->mci_errno);
        }
 
+#ifdef SMTP
        if (mci->mci_state == MCIS_OPEN)
        {
                /* poke the connection to see if it's still alive */
@@ -309,6 +317,7 @@ mci_get(host, m)
                        mci->mci_exitstat = EX_OK;
                        mci->mci_state = MCIS_CLOSED;
                }
+# ifdef DAEMON
                else
                {
                        /* get peer host address for logging reasons only */
@@ -318,13 +327,23 @@ mci_get(host, m)
                        (void) getpeername(fileno(mci->mci_in),
                                (struct sockaddr *) &CurHostAddr, &socksize);
                }
+# endif
        }
+#endif
+#ifdef MAYBE_NEXT_RELEASE
        if (mci->mci_state == MCIS_CLOSED)
        {
-               /* copy out any mailer flags needed in connection state */
-               if (bitnset(M_7BITS, m->m_flags))
-                       mci->mci_flags |= MCIF_7BIT;
+               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;
+               }
        }
+#endif
 
        return mci;
 }
@@ -341,13 +360,14 @@ mci_get(host, m)
 **             none.
 */
 
+void
 mci_dump(mci, logit)
        register MCI *mci;
        bool logit;
 {
        register char *p;
        char *sep;
-       char buf[1000];
+       char buf[4000];
        extern char *ctime();
 
        sep = logit ? " " : "\n\t";
@@ -359,7 +379,7 @@ mci_dump(mci, logit)
                sprintf(p, "NULL");
                goto printit;
        }
-       sprintf(p, "flags=%o, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
+       sprintf(p, "flags=%x, errno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s",
                mci->mci_flags, mci->mci_errno, mci->mci_herrno,
                mci->mci_exitstat, mci->mci_state, mci->mci_pid, sep);
        p += strlen(p);
@@ -375,7 +395,7 @@ mci_dump(mci, logit)
 printit:
 #ifdef LOG
        if (logit)
-               syslog(LOG_DEBUG, "%s", buf);
+               syslog(LOG_DEBUG, "%.1000s", buf);
        else
 #endif
                printf("%s\n", buf);
@@ -391,6 +411,7 @@ printit:
 **             none.
 */
 
+void
 mci_dump_all(logit)
        bool logit;
 {
diff --git a/usr.sbin/sendmail/src/mime.c b/usr.sbin/sendmail/src/mime.c
new file mode 100644 (file)
index 0000000..acbc04c
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+ * Copyright (c) 1994 Eric P. Allman
+ * Copyright (c) 1994
+ *     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.
+ */
+
+# include "sendmail.h"
+# include <string.h>
+
+#ifndef lint
+static char sccsid[] = "@(#)mime.c     8.30 (Berkeley) 10/31/95";
+#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"
+};
+\f/*
+**  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;
+       struct args argv[MAXMIMEARGS];
+       char bbuf[128];
+       char buf[MAXLINE];
+       char pvpbuf[MAXLINE];
+       extern u_char MimeTokenTab[256];
+
+       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");
+       }
+       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.
+       */
+
+       sprintf(buf, "%.100s/%.100s", type, subtype);
+       if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e')))
+               flags |= M87F_NO8BIT;
+
+       /*
+       **  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)
+               {
+                       syserr("mime8to7: Content-Type: %s missing boundary", p);
+                       p = "---";
+               }
+               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;
+               }
+               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");
+               else
+               {
+                       boundaries[i] = bbuf;
+                       boundaries[i + 1] = NULL;
+               }
+               mci->mci_flags |= MCIF_INMIME;
+
+               /* skip the early "comment" prologue */
+               putline("", mci);
+               while (fgets(buf, sizeof buf, e->e_dfp) != NULL)
+               {
+                       bt = mimeboundary(buf, boundaries);
+                       if (bt != MBT_NOTSEP)
+                               break;
+                       putxline(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;
+
+                       sprintf(buf, "--%s", bbuf);
+                       putline(buf, mci);
+                       if (tTd(43, 35))
+                               printf("  ...%s\n", buf);
+                       collect(e->e_dfp, FALSE, 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);
+               }
+               sprintf(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, 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/* 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, 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\n",
+                       sectionhighbits, sectionsize,
+                       cte == NULL ? "[none]" : cte);
+       }
+       if (cte != NULL && strcasecmp(cte, "binary") == 0)
+               sectionsize = sectionhighbits;
+       linelen = 0;
+       bp = buf;
+       if (sectionhighbits == 0)
+       {
+               /* no encoding necessary */
+               if (cte != NULL)
+               {
+                       sprintf(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 (sectionsize / 8 < sectionhighbits)
+       {
+               /* use base64 encoding */
+               int c1, c2;
+
+               putline("Content-Transfer-Encoding: base64", mci);
+               if (tTd(43, 36))
+                       printf("  ...Content-Transfer-Encoding: base64\n");
+               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);
+
+               putline("Content-Transfer-Encoding: quoted-printable", mci);
+               if (tTd(43, 36))
+                       printf("  ...Content-Transfer-Encoding: quoted-printable\n");
+               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;
+}
+\f/*
+**  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(&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++;
+}
+\f/*
+**  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')
+       {
+               sendlf = TRUE;
+               return '\r';
+       }
+       return c;
+}
+\f/*
+**  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;
+
+       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;
+}
+\f/*
+**  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";
+}
+\f/*
+**  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 /* MIME */
index 00621c2..f1bd16a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)parseaddr.c        8.31 (Berkeley) 4/15/94";
+static char sccsid[] = "@(#)parseaddr.c        8.87 (Berkeley) 11/29/95";
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -91,6 +91,7 @@ parseaddr(addr, a, flags, delim, delimptr, e)
        char pvpbuf[PSBUFSIZE];
        extern ADDRESS *buildaddr();
        extern bool invalidaddr();
+       extern void allocaddr __P((ADDRESS *, int, char *));
 
        /*
        **  Initialize and prescan address.
@@ -103,7 +104,7 @@ parseaddr(addr, a, flags, delim, delimptr, e)
        if (delimptr == NULL)
                delimptr = &delimptrbuf;
 
-       pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr);
+       pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
        if (pvp == NULL)
        {
                if (tTd(20, 1))
@@ -172,12 +173,15 @@ parseaddr(addr, a, flags, delim, delimptr, e)
        {
                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)
+               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";
        }
 
        /*
@@ -208,7 +212,7 @@ invalidaddr(addr, delimptr)
        register char *addr;
        char *delimptr;
 {
-       char savedelim;
+       char savedelim = '\0';
 
        if (delimptr != NULL)
        {
@@ -231,14 +235,14 @@ invalidaddr(addr, delimptr)
        }
        if (*addr == '\0')
        {
-               if (savedelim != '\0' && delimptr != NULL)
+               if (delimptr != NULL && savedelim != '\0')
                        *delimptr = savedelim;
                return FALSE;
        }
        setstat(EX_USAGE);
        usrerr("553 Address contained invalid control characters");
   addrfailure:
-       if (savedelim != '\0' && delimptr != NULL)
+       if (delimptr != NULL && savedelim != '\0')
                *delimptr = savedelim;
        return TRUE;
 }
@@ -260,13 +264,14 @@ invalidaddr(addr, delimptr)
 **             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=%o, paddr=%s)\n", flags, paddr);
+               printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
 
        a->q_paddr = paddr;
 
@@ -311,6 +316,8 @@ allocaddr(a, flags, paddr)
 **             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.
@@ -323,8 +330,9 @@ allocaddr(a, flags, paddr)
 # 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       5       /* number of states */
+# define NSTATES       6       /* number of states */
 # define TYPE          017     /* mask to select state type */
 
 /* meta bits for table */
@@ -334,46 +342,102 @@ allocaddr(a, flags, paddr)
 
 static short StateTab[NSTATES][NSTATES] =
 {
-   /*  oldst   chtype> OPR     ATM     QST     SPC     ONE     */
-       /*OPR*/         OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,
-       /*ATM*/         OPR|B,  ATM,    QST|B,  SPC|MB, ONE|B,
-       /*QST*/         QST,    QST,    OPR,    QST,    QST,
-       /*SPC*/         OPR,    ATM,    QST,    SPC|M,  ONE,
-       /*ONE*/         OPR,    OPR,    OPR,    OPR,    OPR,
+   /*  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 TokTypeTab[256] =
+static u_char  TokTypeTab[256] =
 {
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM,ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
-       OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       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   */
+       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,
 };
 
-#define toktype(c)     ((int) TokTypeTab[(c) & 0xff])
+/* 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)
+prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
        char *addr;
-       char delim;
+       int delim;
        char pvpbuf[];
+       int pvpbsize;
        char **delimptr;
+       u_char *toktab;
 {
        register char *p;
        register char *q;
@@ -396,7 +460,14 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                char obuf[50];
 
                firsttime = FALSE;
-               expand("\201o", obuf, &obuf[sizeof obuf - sizeof DELIMCHARS], CurEnv);
+               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++)
                {
@@ -404,6 +475,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                                TokTypeTab[*p & 0xff] = OPR;
                }
        }
+       if (toktab == NULL)
+               toktab = TokTypeTab;
 
        /* make sure error messages don't have garbage on them */
        errno = 0;
@@ -437,6 +510,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                                if (q >= &pvpbuf[pvpbsize - 5])
                                {
                                        usrerr("553 Address too long");
+                                       if (strlen(addr) > MAXNAME)
+                                               addr[MAXNAME] = '\0';
        returnnull:
                                        if (delimptr != NULL)
                                                *delimptr = p;
@@ -546,10 +621,17 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                        if (c == delim && anglecnt <= 0 && state != QST)
                                break;
 
-                       newstate = StateTab[state][toktype(c)];
+                       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))
@@ -642,10 +724,6 @@ struct match
 
 # define MAXMATCH      9       /* max params per rewrite */
 
-# ifndef MAXRULERECURSION
-#  define MAXRULERECURSION     50      /* max recursion depth */
-# endif
-
 
 int
 rewrite(pvp, ruleset, reclevel, e)
@@ -666,7 +744,7 @@ rewrite(pvp, ruleset, reclevel, e)
        struct match mlist[MAXMATCH];   /* stores match on LHS */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
 
-       if (OpMode == MD_TEST || tTd(21, 2))
+       if (OpMode == MD_TEST || tTd(21, 1))
        {
                printf("rewrite: ruleset %2d   input:", ruleset);
                printav(pvp);
@@ -676,9 +754,10 @@ rewrite(pvp, ruleset, reclevel, e)
                syserr("554 rewrite: illegal ruleset number %d", ruleset);
                return EX_CONFIG;
        }
-       if (reclevel++ > MAXRULERECURSION)
+       if (reclevel++ > MaxRuleRecursion)
        {
-               syserr("rewrite: infinite recursion, ruleset %d", ruleset);
+               syserr("rewrite: excessive recursion (max %d), ruleset %d",
+                       MaxRuleRecursion, ruleset);
                return EX_CONFIG;
        }
        if (pvp == NULL)
@@ -740,7 +819,6 @@ rewrite(pvp, ruleset, reclevel, e)
 
                        switch (*rp & 0377)
                        {
-                               register STAB *s;
                                char buf[MAXLINE];
 
                          case MATCHCLASS:
@@ -753,8 +831,7 @@ rewrite(pvp, ruleset, reclevel, e)
                                        goto backup;
                                mlp->last = avp++;
                                cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0');
-                               s = stab(buf, ST_CLASS, ST_FIND);
-                               if (s == NULL || !bitnset(rp[1], s->s_class))
+                               if (!wordinclass(buf, rp[1]))
                                {
                                        if (tTd(21, 36))
                                        {
@@ -773,8 +850,7 @@ rewrite(pvp, ruleset, reclevel, e)
 
                          case MATCHNCLASS:
                                /* match any token not in a class */
-                               s = stab(ap, ST_CLASS, ST_FIND);
-                               if (s != NULL && bitnset(rp[1], s->s_class))
+                               if (wordinclass(ap, rp[1]))
                                        goto backup;
 
                                /* fall through */
@@ -812,8 +888,8 @@ rewrite(pvp, ruleset, reclevel, e)
                                ap = macvalue(rp[1], e);
                                mlp->first = avp;
                                if (tTd(21, 2))
-                                       printf("rewrite: LHS $&%c => \"%s\"\n",
-                                               rp[1],
+                                       printf("rewrite: LHS $&%s => \"%s\"\n",
+                                               macname(rp[1]),
                                                ap == NULL ? "(NULL)" : ap);
 
                                if (ap == NULL)
@@ -984,8 +1060,8 @@ rewrite(pvp, ruleset, reclevel, e)
                                {
                                        *avp = macvalue(rp[1], e);
                                        if (tTd(21, 2))
-                                               printf("rewrite: RHS $&%c => \"%s\"\n",
-                                                       rp[1],
+                                               printf("rewrite: RHS $&%s => \"%s\"\n",
+                                                       macname(rp[1]),
                                                        *avp == NULL ? "(NULL)" : *avp);
                                        if (*avp != NULL)
                                                avp++;
@@ -1097,10 +1173,22 @@ rewrite(pvp, ruleset, reclevel, e)
                        /* look it up */
                        cataddr(key_rvp, NULL, buf, sizeof buf, '\0');
                        argvect[0] = buf;
-                       if (map != NULL && bitset(MF_OPEN, map->s_map.map_mflags))
+                       if (e->e_sendmode == SM_DEFER)
+                       {
+                               /* don't do any map lookups */
+                               if (tTd(60, 1))
+                                       printf("map_lookup(%s, %s) => DEFERRED\n",
+                                               mapname, buf);
+                               replac = NULL;
+                               rstat = EX_TEMPFAIL;
+                       }
+                       else if (map != NULL && bitset(MF_OPEN, map->s_map.map_mflags))
                        {
                                auto int stat = EX_OK;
 
+                               if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags))
+                                       stripquotes(buf);
+
                                /* XXX should try to auto-open the map here */
 
                                if (tTd(60, 1))
@@ -1115,7 +1203,21 @@ rewrite(pvp, ruleset, reclevel, e)
 
                                /* should recover if stat == EX_TEMPFAIL */
                                if (stat == EX_TEMPFAIL)
-                                       rstat = stat;
+                               {
+                                       rstat = EX_TEMPFAIL;
+                                       if (tTd(60, 1))
+                                               printf("map_lookup(%s, %s) tempfail: errno=%d\n",
+                                                       mapname, buf, errno);
+                                       if (e->e_message == NULL)
+                                       {
+                                               char mbuf[300];
+
+                                               sprintf(mbuf, "%.80s map: lookup (%s): deferred",
+                                                       mapname,
+                                                       shortenstring(buf, 203));
+                                               e->e_message = newstr(mbuf);
+                                       }
+                               }
                        }
                        else
                                replac = NULL;
@@ -1142,7 +1244,7 @@ rewrite(pvp, ruleset, reclevel, e)
                        {
                                /* scan the new replacement */
                                xpvp = prescan(replac, '\0', pvpbuf,
-                                              sizeof pvpbuf, NULL);
+                                              sizeof pvpbuf, NULL, NULL);
                                if (xpvp == NULL)
                                {
                                        /* prescan already printed error */
@@ -1182,11 +1284,15 @@ rewrite(pvp, ruleset, reclevel, e)
                        }
                        else
                        {
+                               int ruleset;
+                               STAB *s;
+
                                bcopy((char *) &npvp[2], (char *) pvp,
                                        (int) (avp - npvp - 2) * sizeof *avp);
                                if (tTd(21, 3))
                                        printf("-----callsubr %s\n", npvp[1]);
-                               stat = rewrite(pvp, atoi(npvp[1]), reclevel, e);
+                               ruleset = strtorwset(npvp[1], NULL, ST_FIND);
+                               stat = rewrite(pvp, ruleset, reclevel, e);
                                if (rstat == EX_OK || stat == EX_TEMPFAIL)
                                        rstat = stat;
                                if (*pvp != NULL && (**pvp & 0377) == CANONNET)
@@ -1205,7 +1311,7 @@ rewrite(pvp, ruleset, reclevel, e)
                }
        }
 
-       if (OpMode == MD_TEST || tTd(21, 2))
+       if (OpMode == MD_TEST || tTd(21, 1))
        {
                printf("rewrite: ruleset %2d returns:", ruleset);
                printav(pvp);
@@ -1260,15 +1366,17 @@ buildaddr(tv, a, flags, e)
 {
        struct mailer **mp;
        register struct mailer *m;
-       char *bp;
-       int spaceleft;
+       register char *p;
+       char *mname;
+       char **hostp;
+       char hbuf[MAXNAME + 1];
        static MAILER errormailer;
        static char *errorargv[] = { "ERROR", NULL };
-       static char buf[MAXNAME];
+       static char ubuf[MAXNAME + 1];
 
        if (tTd(24, 5))
        {
-               printf("buildaddr, flags=%o, tv=", flags);
+               printf("buildaddr, flags=%x, tv=", flags);
                printav(tv);
        }
 
@@ -1276,10 +1384,13 @@ buildaddr(tv, a, flags, e)
                a = (ADDRESS *) xalloc(sizeof *a);
        bzero((char *) a, sizeof *a);
 
+       /* set up default error return flags */
+       a->q_flags |= QPINGONFAILURE|QPINGONDELAY;
+
        /* figure out what net/mailer to use */
        if (*tv == NULL || (**tv & 0377) != CANONNET)
        {
-               syserr("554 buildaddr: no net");
+               syserr("554 buildaddr: no mailer in parsed address");
 badaddr:
                a->q_flags |= QBADADDR;
                a->q_mailer = &errormailer;
@@ -1292,91 +1403,97 @@ badaddr:
                }
                return a;
        }
-       tv++;
-       if (strcasecmp(*tv, "error") == 0)
+       mname = *++tv;
+
+       /* extract host and user portions */
+       if ((**++tv & 0377) == CANONHOST)
+               hostp = ++tv;
+       else
+               hostp = NULL;
+       while (*tv != NULL && (**tv & 0377) != CANONUSER)
+               tv++;
+       if (*tv == NULL)
        {
-               if ((**++tv & 0377) == CANONHOST)
+               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 (isascii(**++tv) && isdigit(**tv))
+                       if (strchr(hbuf, '.') != NULL)
+                       {
+                               a->q_status = newstr(hbuf);
+                               setstat(dsntoexitstat(hbuf));
+                       }
+                       else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
                        {
-                               setstat(atoi(*tv));
+                               setstat(atoi(hbuf));
                        }
                        else
                        {
                                for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
-                                       if (strcasecmp(ep->ec_name, *tv) == 0)
+                                       if (strcasecmp(ep->ec_name, hbuf) == 0)
                                                break;
                                setstat(ep->ec_code);
                        }
-                       tv++;
                }
                else
                        setstat(EX_UNAVAILABLE);
-               if ((**tv & 0377) != CANONUSER)
-                       syserr("554 buildaddr: error: no user");
-               cataddr(++tv, NULL, buf, sizeof buf, ' ');
-               stripquotes(buf);
-               if (isascii(buf[0]) && isdigit(buf[0]) &&
-                   isascii(buf[1]) && isdigit(buf[1]) &&
-                   isascii(buf[2]) && isdigit(buf[2]) &&
-                   buf[3] == ' ')
+               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, buf, 3);
+                       strncpy(fmt, ubuf, 3);
                        strcpy(&fmt[3], " %s");
-                       usrerr(fmt, buf + 4);
+                       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", buf);
+                       usrerr("553 %s", ubuf);
                }
                goto badaddr;
        }
 
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
-               if (strcasecmp(m->m_name, *tv) == 0)
+               if (strcasecmp(m->m_name, mname) == 0)
                        break;
        }
        if (m == NULL)
        {
-               syserr("554 buildaddr: unknown mailer %s", *tv);
+               syserr("554 buildaddr: unknown mailer %s", mname);
                goto badaddr;
        }
        a->q_mailer = m;
 
        /* figure out what host (if any) */
-       tv++;
-       if ((**tv & 0377) == CANONHOST)
-       {
-               bp = buf;
-               spaceleft = sizeof buf - 1;
-               while (*++tv != NULL && (**tv & 0377) != CANONUSER)
-               {
-                       int i = strlen(*tv);
-
-                       if (i > spaceleft)
-                       {
-                               /* out of space for this address */
-                               if (spaceleft >= 0)
-                                       syserr("554 buildaddr: host too long (%.40s...)",
-                                               buf);
-                               i = spaceleft;
-                               spaceleft = 0;
-                       }
-                       if (i <= 0)
-                               continue;
-                       bcopy(*tv, bp, i);
-                       bp += i;
-                       spaceleft -= i;
-               }
-               *bp = '\0';
-               a->q_host = newstr(buf);
-       }
-       else
+       if (hostp == NULL)
        {
                if (!bitnset(M_LOCALMAILER, m->m_flags))
                {
@@ -1385,47 +1502,38 @@ badaddr:
                }
                a->q_host = NULL;
        }
+       else
+               a->q_host = newstr(hbuf);
 
        /* figure out the user */
-       if (*tv == NULL || (**tv & 0377) != CANONUSER)
+       p = ubuf;
+       if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
        {
-               syserr("554 buildaddr: no user");
-               goto badaddr;
+               p++;
+               tv++;
+               a->q_flags |= QNOTREMOTE;
        }
-       tv++;
 
        /* do special mapping for local mailer */
-       if (m == LocalMailer && *tv != NULL)
+       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))
        {
-               register char *p = *tv;
-
-               if (*p == '"')
-                       p++;
-               if (*p == '|')
-                       a->q_mailer = m = ProgMailer;
-               else if (*p == '/')
-                       a->q_mailer = m = FileMailer;
-               else if (*p == ':')
+               /* may be :include: */
+               stripquotes(ubuf);
+               if (strncasecmp(ubuf, ":include:", 9) == 0)
                {
-                       /* may be :include: */
-                       cataddr(tv, NULL, buf, sizeof buf, '\0');
-                       stripquotes(buf);
-                       if (strncasecmp(buf, ":include:", 9) == 0)
-                       {
-                               /* if :include:, don't need further rewriting */
-                               a->q_mailer = m = InclMailer;
-                               a->q_user = &buf[9];
-                               return (a);
-                       }
+                       /* if :include:, don't need further rewriting */
+                       a->q_mailer = m = InclMailer;
+                       a->q_user = newstr(&ubuf[9]);
+                       return a;
                }
        }
 
-       if (m == LocalMailer && *tv != NULL && strcmp(*tv, "@") == 0)
-       {
-               tv++;
-               a->q_flags |= QNOTREMOTE;
-       }
-
        /* rewrite according recipient mailer rewriting rules */
        define('h', a->q_host, e);
        if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
@@ -1438,8 +1546,8 @@ badaddr:
        (void) rewrite(tv, 4, 0, e);
 
        /* save the result for the command line/RCPT argument */
-       cataddr(tv, NULL, buf, sizeof buf, '\0');
-       a->q_user = buf;
+       cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
+       a->q_user = ubuf;
 
        /*
        **  Do mapping to lower case as requested by mailer
@@ -1450,7 +1558,12 @@ badaddr:
        if (!bitnset(M_USR_UPPER, m->m_flags))
                makelower(a->q_user);
 
-       return (a);
+       if (tTd(24, 6))
+       {
+               printf("buildaddr => ");
+               printaddr(a, FALSE);
+       }
+       return a;
 }
 \f/*
 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
@@ -1471,12 +1584,13 @@ badaddr:
 **             Destroys buf.
 */
 
+void
 cataddr(pvp, evp, buf, sz, spacesub)
        char **pvp;
        char **evp;
        char *buf;
        register int sz;
-       char spacesub;
+       int spacesub;
 {
        bool oatomtok = FALSE;
        bool natomtok = FALSE;
@@ -1495,7 +1609,7 @@ cataddr(pvp, evp, buf, sz, spacesub)
        sz -= 2;
        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
        {
-               natomtok = (toktype(**pvp) == ATM);
+               natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
                if (oatomtok && natomtok)
                        *p++ = spacesub;
                (void) strcpy(p, *pvp);
@@ -1580,17 +1694,55 @@ sameaddr(a, b)
 **             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,
+       NULL
+};
+
+void
 printaddr(a, follow)
        register ADDRESS *a;
        bool follow;
 {
-       bool first = TRUE;
        register MAILER *m;
        MAILER pseudomailer;
+       register struct qflags *qfp;
+       bool firstone;
+
+       if (a == NULL)
+       {
+               printf("[NULL]\n");
+               return;
+       }
 
        while (a != NULL)
        {
-               first = FALSE;
                printf("%x=", a);
                (void) fflush(stdout);
 
@@ -1603,25 +1755,60 @@ printaddr(a, follow)
                        m->m_name = "NULL";
                }
 
-               printf("%s:\n\tmailer %d (%s), host `%s', user `%s', ruser `%s'\n",
-                      a->q_paddr, m->m_mno, m->m_name,
-                      a->q_host, a->q_user,
-                      a->q_ruser ? a->q_ruser : "<null>");
-               printf("\tnext=%x, flags=%o, alias %x, uid %d, gid %d\n",
-                      a->q_next, a->q_flags, a->q_alias, a->q_uid, a->q_gid);
+               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=%x, alias %x, uid %d, gid %d\n",
+                      a->q_next, a->q_alias, a->q_uid, 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, rstatus=%s\n",
+                      a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
+                      a->q_statmta == NULL ? "(none)" : a->q_statmta,
+                      a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
 
                if (!follow)
                        return;
                a = a->q_next;
        }
-       if (first)
-               printf("[NULL]\n");
 }
+\f/*
+**  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;
+}
 \f/*
 **  REMOTENAME -- return the name relative to the current mailer
 **
@@ -1657,8 +1844,8 @@ remotename(name, m, flags, pstat, e)
        char *fancy;
        char *oldg = macvalue('g', e);
        int rwset;
-       static char buf[MAXNAME];
-       char lbuf[MAXNAME];
+       static char buf[MAXNAME + 1];
+       char lbuf[MAXNAME + 1];
        char pvpbuf[PSBUFSIZE];
        extern char *crackaddr();
 
@@ -1693,7 +1880,7 @@ remotename(name, m, flags, pstat, e)
        **      domain will be appended.
        */
 
-       pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL);
+       pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
        if (pvp == NULL)
                return (name);
        if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
@@ -1760,9 +1947,9 @@ remotename(name, m, flags, pstat, e)
 
        /* need to make sure route-addrs have <angle brackets> */
        if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
-               expand("<\201g>", buf, &buf[sizeof buf - 1], e);
+               expand("<\201g>", buf, sizeof buf, e);
        else
-               expand(fancy, buf, &buf[sizeof buf - 1], e);
+               expand(fancy, buf, sizeof buf, e);
 
        define('g', oldg, e);
 
@@ -1777,14 +1964,18 @@ remotename(name, m, flags, pstat, e)
 **             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.
 */
 
-maplocaluser(a, sendq, e)
+void
+maplocaluser(a, sendq, aliaslevel, e)
        register ADDRESS *a;
        ADDRESS **sendq;
+       int aliaslevel;
        ENVELOPE *e;
 {
        register char **pvp;
@@ -1797,7 +1988,7 @@ maplocaluser(a, sendq, e)
                printf("maplocaluser: ");
                printaddr(a, FALSE);
        }
-       pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr);
+       pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
        if (pvp == NULL)
                return;
 
@@ -1818,8 +2009,8 @@ maplocaluser(a, sendq, e)
                printaddr(a, FALSE);
        }
        a1->q_alias = a;
-       allocaddr(a1, RF_COPYALL, NULL);
-       (void) recipient(a1, sendq, e);
+       allocaddr(a1, RF_COPYALL, a->q_paddr);
+       (void) recipient(a1, sendq, aliaslevel, e);
 }
 \f/*
 **  DEQUOTE_INIT -- initialize dequote map
@@ -1841,6 +2032,7 @@ dequote_init(map, args)
 {
        register char *p = args;
 
+       map->map_mflags |= MF_KEEPQUOTES;
        for (;;)
        {
                while (isascii(*p) && isspace(*p))
@@ -1852,6 +2044,10 @@ dequote_init(map, args)
                  case 'a':
                        map->map_app = ++p;
                        break;
+
+                 case 's':
+                       map->map_coldelim = *++p;
+                       break;
                }
                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
                        p++;
@@ -1888,19 +2084,13 @@ dequote_map(map, name, av, statp)
        register char *p;
        register char *q;
        register char c;
-       int anglecnt;
-       int cmntcnt;
-       int quotecnt;
-       int spacecnt;
-       bool quotemode;
-       bool bslashmode;
-
-       anglecnt = 0;
-       cmntcnt = 0;
-       quotecnt = 0;
-       spacecnt = 0;
-       quotemode = FALSE;
-       bslashmode = FALSE;
+       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'; )
        {
@@ -1911,6 +2101,9 @@ dequote_map(map, name, av, statp)
                        continue;
                }
 
+               if (c == ' ' && spacesub != '\0')
+                       c = spacesub;
+
                switch (c)
                {
                  case '\\':
index a611c0b..a364fba 100644 (file)
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     @(#)pathnames.h 8.2 (Berkeley) 8/20/93
+ *     @(#)pathnames.h 8.4 (Berkeley) 6/19/95
  */
 
 #ifndef _PATH_SENDMAILCF
-# define _PATH_SENDMAILCF      "/etc/sendmail.cf"
+# 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
@@ -44,3 +48,7 @@
 #  define _PATH_SENDMAILPID    "/etc/sendmail.pid"
 # endif
 #endif
+
+#ifndef _PATH_HOSTS
+# define _PATH_HOSTS           "/etc/hosts"
+#endif
index a532416..97bf36c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
 
 #ifndef lint
 #ifdef QUEUE
-static char sccsid[] = "@(#)queue.c    8.41.1.3 (Berkeley) 3/5/95 (with queueing)";
+static char sccsid[] = "@(#)queue.c    8.98 (Berkeley) 11/11/95 (with queueing)";
 #else
-static char sccsid[] = "@(#)queue.c    8.41.1.3 (Berkeley) 3/5/95 (without queueing)";
+static char sccsid[] = "@(#)queue.c    8.98 (Berkeley) 11/11/95 (without queueing)";
 #endif
 #endif /* not lint */
 
 # include <errno.h>
-# include <pwd.h>
 # include <dirent.h>
 
 # ifdef QUEUE
@@ -55,6 +54,9 @@ static char sccsid[] = "@(#)queue.c   8.41.1.3 (Berkeley) 3/5/95 (without queueing
 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 */
@@ -63,13 +65,17 @@ struct work
 typedef struct work    WORK;
 
 WORK   *WorkQ;                 /* queue of things to be done */
+
+#define QF_VERSION     1       /* version number of this queue format */
+
+#if !defined(NGROUPS_MAX) && defined(NGROUPS)
+# define NGROUPS_MAX   NGROUPS /* POSIX naming convention */
+#endif
 \f/*
 **  QUEUEUP -- queue a message up for future transmission.
 **
 **     Parameters:
 **             e -- the envelope to queue up.
-**             queueall -- if TRUE, queue all addresses, rather than
-**                     just those with the QQUEUEUP flag set.
 **             announce -- if TRUE, tell when you are queueing up.
 **
 **     Returns:
@@ -80,9 +86,9 @@ WORK  *WorkQ;                 /* queue of things to be done */
 **             The queue file is left locked.
 */
 
-queueup(e, queueall, announce)
+void
+queueup(e, announce)
        register ENVELOPE *e;
-       bool queueall;
        bool announce;
 {
        char *qf;
@@ -96,6 +102,7 @@ queueup(e, queueall, announce)
        MAILER nullmailer;
        MCI mcibuf;
        char buf[MAXLINE], tf[MAXLINE];
+       extern void printctladdr __P((ADDRESS *, FILE *));
 
        /*
        **  Create control file.
@@ -157,6 +164,18 @@ queueup(e, queueall, announce)
        if (tTd(40, 1))
                printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id,
                        newid ? " (new id)" : "");
+       if (tTd(40, 3))
+       {
+               extern void printenvflags();
+
+               printf("  e_flags=");
+               printenvflags(e);
+       }
+       if (tTd(40, 32))
+       {
+               printf("  sendq=");
+               printaddr(e->e_sendqueue, TRUE);
+       }
        if (tTd(40, 9))
        {
                printf("  tfp=");
@@ -172,17 +191,25 @@ queueup(e, queueall, announce)
        **  If there is no data file yet, create one.
        */
 
-       if (e->e_df == NULL)
+       if (!bitset(EF_HAS_DF, e->e_flags))
        {
                register FILE *dfp;
-               extern putbody();
+               char dfname[20];
+               struct stat stbuf;
 
-               e->e_df = queuename(e, 'd');
-               e->e_df = newstr(e->e_df);
-               fd = open(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
+               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",
-                               e->e_df, geteuid());
+                               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;
@@ -197,16 +224,30 @@ queueup(e, queueall, announce)
        **      they are required by orderq.
        */
 
-       /* output message priority */
-       fprintf(tfp, "P%ld\n", e->e_msgpriority);
+       /* output queue version number (must be first!) */
+       fprintf(tfp, "V%d\n", QF_VERSION);
 
        /* output creation time */
        fprintf(tfp, "T%ld\n", e->e_ctime);
 
-       /* output type and name of data file */
+       /* output last delivery time */
+       fprintf(tfp, "K%ld\n", 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)
+               fprintf(tfp, "I%d/%d/%ld\n",
+                       major(e->e_dfdev), minor(e->e_dfdev), e->e_dfino);
+
+       /* output body type */
        if (e->e_bodytype != NULL)
                fprintf(tfp, "B%s\n", e->e_bodytype);
-       fprintf(tfp, "D%s\n", e->e_df);
 
        /* message from envelope, if it exists */
        if (e->e_message != NULL)
@@ -218,6 +259,8 @@ queueup(e, queueall, announce)
                *p++ = 'w';
        if (bitset(EF_RESPONSE, e->e_flags))
                *p++ = 'r';
+       if (bitset(EF_HAS8BIT, e->e_flags))
+               *p++ = '8';
        *p++ = '\0';
        if (buf[0] != '\0')
                fprintf(tfp, "F%s\n", buf);
@@ -231,33 +274,45 @@ queueup(e, queueall, announce)
                fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE));
 
        /* output name of sender */
-       fprintf(tfp, "S%s\n", denlstring(e->e_from.q_paddr, TRUE, FALSE));
+       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 list of error recipients */
-       printctladdr(NULL, NULL);
-       for (q = e->e_errorqueue; q != NULL; q = q->q_next)
-       {
-               if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
-               {
-                       printctladdr(q, tfp);
-                       fprintf(tfp, "E%s\n", denlstring(q->q_paddr, 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(QQUEUEUP, q->q_flags) ||
-                   (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)))
+                   !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))
                {
                        printctladdr(q, tfp);
-                       fprintf(tfp, "R%s\n", denlstring(q->q_paddr, TRUE, FALSE));
+                       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(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(NULL, NULL, "queued", NULL, e);
+                                       logdelivery(q->q_mailer, NULL, "queued",
+                                                   NULL, (time_t) 0, e);
                                e->e_to = NULL;
                        }
                        if (tTd(40, 1))
@@ -302,7 +357,7 @@ queueup(e, queueall, announce)
                /* expand macros; if null, don't output header at all */
                if (bitset(H_DEFAULT, h->h_flags))
                {
-                       (void) expand(h->h_value, buf, &buf[sizeof buf], e);
+                       (void) expand(h->h_value, buf, sizeof buf, e);
                        if (buf[0] == '\0')
                                continue;
                }
@@ -347,8 +402,13 @@ queueup(e, queueall, announce)
 
        /*
        **  Clean up.
+       **
+       **      Write a terminator record -- this is to prevent
+       **      scurrilous crackers from appending any data.
        */
 
+       fprintf(tfp, ".\n");
+
        if (fflush(tfp) < 0 || fsync(fileno(tfp)) < 0 || ferror(tfp))
        {
                if (newid)
@@ -362,8 +422,8 @@ queueup(e, queueall, announce)
                /* rename (locked) tf to be (locked) qf */
                qf = queuename(e, 'q');
                if (rename(tf, qf) < 0)
-                       syserr("cannot rename(%s, %s), df=%s, uid=%d",
-                               tf, qf, e->e_df, geteuid());
+                       syserr("cannot rename(%s, %s), uid=%d",
+                               tf, qf, geteuid());
 
                /* close and unlock old (locked) qf */
                if (e->e_lockfp != NULL)
@@ -378,7 +438,7 @@ queueup(e, queueall, announce)
 # ifdef LOG
        /* save log info */
        if (LogLevel > 79)
-               syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
+               syslog(LOG_DEBUG, "%s: queueup, qf=%s", e->e_id, qf);
 # endif /* LOG */
 
        if (tTd(40, 1))
@@ -386,6 +446,7 @@ queueup(e, queueall, announce)
        return;
 }
 
+void
 printctladdr(a, tfp)
        register ADDRESS *a;
        FILE *tfp;
@@ -422,14 +483,13 @@ printctladdr(a, tfp)
        lastuid = uid;
        lastctladdr = a;
 
-       if (uid == 0 || (pw = getpwuid(uid)) == NULL)
+       if (uid == 0 || (pw = sm_getpwuid(uid)) == NULL)
                uname = "";
        else
                uname = pw->pw_name;
 
        fprintf(tfp, "C%s:%s\n", uname, denlstring(a->q_paddr, TRUE, FALSE));
 }
-
 \f/*
 **  RUNQUEUE -- run the jobs in the queue.
 **
@@ -450,10 +510,13 @@ printctladdr(a, tfp)
 
 ENVELOPE       QueueEnvelope;          /* the queue run envelope */
 
+void
 runqueue(forkflag)
        bool forkflag;
 {
        register ENVELOPE *e;
+       int njobs;
+       int sequenceno = 0;
        extern ENVELOPE BlankEnvelope;
 
        /*
@@ -465,8 +528,14 @@ runqueue(forkflag)
 
        if (shouldqueue(0L, curtime()))
        {
+               char *msg = "Skipping queue run -- load average too high";
+
                if (Verbose)
-                       printf("Skipping queue run -- load average too high\n");
+                       printf("%s\n", msg);
+#ifdef LOG
+               if (LogLevel > 8)
+                       syslog(LOG_INFO, "runqueue: %s", msg);
+#endif
                if (forkflag && QueueIntvl != 0)
                        (void) setevent(QueueIntvl, runqueue, TRUE);
                return;
@@ -479,6 +548,7 @@ runqueue(forkflag)
        if (forkflag)
        {
                int pid;
+               extern void intsig();
 #ifdef SIGCHLD
                extern void reapchild();
 
@@ -491,18 +561,21 @@ runqueue(forkflag)
                        /* parent -- pick up intermediate zombie */
 #ifndef SIGCHLD
                        (void) waitfor(pid);
+#else
+                       CurChildren++;
 #endif /* SIGCHLD */
                        if (QueueIntvl != 0)
                                (void) setevent(QueueIntvl, runqueue, TRUE);
                        return;
                }
-               /* child -- double fork */
+               /* child -- double fork and clean up signals */
 #ifndef SIGCHLD
                if (fork() != 0)
                        exit(EX_OK);
 #else /* SIGCHLD */
                (void) setsignal(SIGCHLD, SIG_DFL);
 #endif /* SIGCHLD */
+               (void) setsignal(SIGHUP, intsig);
        }
 
        setproctitle("running queue: %s", QueueDir);
@@ -546,7 +619,7 @@ runqueue(forkflag)
        */
 
        /* order the existing work requests */
-       (void) orderq(FALSE);
+       njobs = orderq(FALSE);
 
        /* process them once at a time */
        while (WorkQ != NULL)
@@ -559,22 +632,29 @@ runqueue(forkflag)
                **  Ignore jobs that are too expensive for the moment.
                */
 
+               sequenceno++;
                if (shouldqueue(w->w_pri, w->w_ctime))
                {
                        if (Verbose)
-                               printf("\nSkipping %s\n", w->w_name + 2);
+                               printf("\nSkipping %s (sequence %d of %d)\n",
+                                       w->w_name + 2, sequenceno, njobs);
                }
                else
                {
                        pid_t pid;
                        extern pid_t dowork();
 
+                       if (Verbose)
+                               printf("\nRunning %s (sequence %d of %d)\n",
+                                       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);
        }
 
@@ -604,6 +684,10 @@ runqueue(forkflag)
 # define NEED_R                004
 # define NEED_S                010
 
+static WORK    *WorkList = NULL;
+static int     WorkListSize = 0;
+
+int
 orderq(doall)
        bool doall;
 {
@@ -611,9 +695,8 @@ orderq(doall)
        register WORK *w;
        DIR *f;
        register int i;
-       WORK wlist[QUEUESIZE+1];
        int wn = -1;
-       extern workcmpf();
+       int wc;
 
        if (tTd(41, 1))
        {
@@ -633,6 +716,8 @@ orderq(doall)
 
                WorkQ = nw;
                free(w->w_name);
+               if (w->w_host)
+                       free(w->w_host);
                free((char *) w);
                w = nw;
        }
@@ -653,9 +738,12 @@ orderq(doall)
        {
                FILE *cf;
                register char *p;
-               char lbuf[MAXNAME];
+               char lbuf[MAXNAME + 1];
                extern bool strcontainedin();
 
+               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;
@@ -664,6 +752,7 @@ orderq(doall)
                    !strcontainedin(QueueLimitId, d->d_name))
                        continue;
 
+#ifdef PICKY_QF_NAME_CHECK
                /*
                **  Check queue name for plausibility.  This handles
                **  both old and new type ids.
@@ -682,22 +771,38 @@ orderq(doall)
                {
                        if (Verbose)
                                printf("orderq: bogus qf name %s\n", d->d_name);
-#ifdef LOG
-                       if (LogLevel > 3)
-                               syslog(LOG_CRIT, "orderq: bogus qf name %s",
+# ifdef LOG
+                       if (LogLevel > 0)
+                               syslog(LOG_ALERT, "orderq: bogus qf name %s",
                                        d->d_name);
-#endif
-                       if (strlen(d->d_name) >= MAXNAME)
-                               d->d_name[MAXNAME - 1] = '\0';
+# endif
+                       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
 
-               /* yes -- open control file (if not too many files) */
-               if (++wn >= QUEUESIZE)
+               /* open control file (if not too many files) */
+               if (++wn >= MaxQueueRun && MaxQueueRun > 0)
+               {
+# ifdef LOG
+                       if (wn == MaxQueueRun && LogLevel > 0)
+                               syslog(LOG_ALERT, "WorkList for %s maxed out at %d",
+                                               QueueDir, MaxQueueRun);
+# endif
                        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)
@@ -705,14 +810,17 @@ orderq(doall)
                        /* this may be some random person sending hir msgs */
                        /* syserr("orderq: cannot open %s", cbuf); */
                        if (tTd(41, 2))
-                               printf("orderq: cannot open %s (%d)\n",
-                                       d->d_name, errno);
+                               printf("orderq: cannot open %s: %s\n",
+                                       d->d_name, errstring(errno));
                        errno = 0;
                        wn--;
                        continue;
                }
-               w = &wlist[wn];
+               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;
@@ -722,11 +830,10 @@ orderq(doall)
                i = NEED_P | NEED_T;
                if (QueueLimitSender != NULL)
                        i |= NEED_S;
-               if (QueueLimitRecipient != NULL)
+               if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL)
                        i |= NEED_R;
                while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
                {
-                       extern long atol();
                        extern bool strcontainedin();
 
                        switch (lbuf[0])
@@ -742,7 +849,10 @@ orderq(doall)
                                break;
 
                          case 'R':
-                               if (QueueLimitRecipient != NULL &&
+                               if (w->w_host == NULL &&
+                                   (p = strrchr(&lbuf[1], '@')) != NULL)
+                                       w->w_host = newstr(&p[1]);
+                               if (QueueLimitRecipient == NULL ||
                                    strcontainedin(QueueLimitRecipient, &lbuf[1]))
                                        i &= ~NEED_R;
                                break;
@@ -752,6 +862,16 @@ orderq(doall)
                                    strcontainedin(QueueLimitSender, &lbuf[1]))
                                        i &= ~NEED_S;
                                break;
+
+                         case 'K':
+                               if ((curtime() - (time_t) atol(&lbuf[1])) < MinQueueAge)
+                                       w->w_tooyoung = TRUE;
+                               break;
+
+                         case 'N':
+                               if (atol(&lbuf[1]) == 0)
+                                       w->w_tooyoung = FALSE;
+                               break;
                        }
                }
                (void) fclose(cf);
@@ -760,17 +880,78 @@ orderq(doall)
                    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++;
 
-       /*
-       **  Sort the work directory.
-       */
+       wc = min(wn, WorkListSize);
+       if (wc > MaxQueueRun && MaxQueueRun > 0)
+               wc = MaxQueueRun;
+
+       if (QueueSortOrder == QS_BYHOST)
+       {
+               extern workcmpf1();
+               extern 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)
+                       {
+                               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 &&
+                                        strcmp(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
+       {
+               extern workcmpf0();
+
+               /*
+               **  Simple sort based on queue priority only.
+               */
 
-       qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf);
+               qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0);
+       }
 
        /*
        **  Convert the work list into canonical form.
@@ -778,15 +959,21 @@ orderq(doall)
        */
 
        WorkQ = NULL;
-       for (i = min(wn, QUEUESIZE); --i >= 0; )
+       for (i = wc; --i >= 0; )
        {
                w = (WORK *) xalloc(sizeof *w);
-               w->w_name = wlist[i].w_name;
-               w->w_pri = wlist[i].w_pri;
-               w->w_ctime = wlist[i].w_ctime;
+               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;
 
        if (tTd(40, 1))
        {
@@ -797,7 +984,59 @@ orderq(doall)
        return (wn);
 }
 \f/*
-**  WORKCMPF -- compare function for ordering work.
+**  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;
+# ifdef LOG
+                       if (LogLevel > 1)
+                       {
+                               syslog(LOG_NOTICE, "grew WorkList for %s to %d",
+                                               QueueDir, WorkListSize);
+                       }
+               }
+               else if (LogLevel > 0)
+               {
+                       syslog(LOG_ALERT, "FAILED to grow WorkList for %s to %d",
+                                       QueueDir, newsize);
+# endif
+               }
+       }
+       if (tTd(41, 1))
+               printf("grow_wlist: WorkListSize now %d\n", WorkListSize);
+}
+\f/*
+**  WORKCMPF0 -- simple priority-only compare function.
 **
 **     Parameters:
 **             a -- the first argument.
@@ -812,7 +1051,8 @@ orderq(doall)
 **             none.
 */
 
-workcmpf(a, b)
+int
+workcmpf0(a, b)
        register WORK *a;
        register WORK *b;
 {
@@ -820,11 +1060,93 @@ workcmpf(a, b)
        long pb = b->w_pri;
 
        if (pa == pb)
-               return (0);
+               return 0;
        else if (pa > pb)
-               return (1);
+               return 1;
        else
-               return (-1);
+               return -1;
+}
+\f/*
+**  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;
+
+       /* 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 = strcmp(a->w_host, b->w_host)))
+               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;
+}
+\f/*
+**  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;
+
+       /* 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 = strcmp(a->w_host, b->w_host)))
+               return i;
+
+       /* job priority */
+       return a->w_pri - b->w_pri;
 }
 \f/*
 **  DOWORK -- do a work request.
@@ -895,6 +1217,7 @@ dowork(id, forkflag, requeueflag, e)
                (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;
@@ -928,7 +1251,7 @@ dowork(id, forkflag, requeueflag, e)
                eatheader(e, requeueflag);
 
                if (requeueflag)
-                       queueup(e, TRUE, FALSE);
+                       queueup(e, FALSE);
 
                /* do the delivery */
                sendall(e, SM_DELIVER);
@@ -964,10 +1287,14 @@ readqf(e)
        ADDRESS *ctladdr;
        struct stat st;
        char *bp;
+       int qfver = 0;
+       register char *p;
+       char *orcpt = NULL;
+       bool nomore = FALSE;
        char qf[20];
        char buf[MAXLINE];
-       extern long atol();
        extern ADDRESS *setctluser();
+       extern void loseqfile();
 
        /*
        **  Read and process the file.
@@ -988,9 +1315,7 @@ readqf(e)
        if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
        {
                /* being processed by another queuer */
-               if (tTd(40, 8))
-                       printf("readqf(%s): locked\n", qf);
-               if (Verbose)
+               if (Verbose || tTd(40, 8))
                        printf("%s: locked\n", e->e_id);
 # ifdef LOG
                if (LogLevel > 19)
@@ -1014,7 +1339,7 @@ readqf(e)
                return FALSE;
        }
 
-       if (st.st_uid != geteuid())
+       if (st.st_uid != geteuid() || bitset(S_IWOTH|S_IWGRP, st.st_mode))
        {
 # ifdef LOG
                if (LogLevel > 0)
@@ -1025,7 +1350,7 @@ readqf(e)
 # endif /* LOG */
                if (tTd(40, 8))
                        printf("readqf(%s): bogus file\n", qf);
-               rename(qf, queuename(e, 'Q'));
+               loseqfile(e, "bogus file uid in mqueue");
                fclose(qfp);
                return FALSE;
        }
@@ -1059,32 +1384,91 @@ readqf(e)
        LineNumber = 0;
        e->e_flags |= EF_GLOBALERRS;
        OpMode = MD_DELIVER;
-       if (Verbose)
-               printf("\nRunning %s\n", e->e_id);
        ctladdr = NULL;
+       e->e_dfino = -1;
+       e->e_msgsize = -1;
        while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
        {
                register char *p;
-               struct stat st;
+               u_long qflags;
+               ADDRESS *q;
 
                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)
+                       {
+                               syserr("Version number in qf (%d) greater than max (%d)",
+                                       qfver, QF_VERSION);
+                       }
+                       break;
+
                  case 'C':             /* specify controlling user */
                        ctladdr = setctluser(&bp[1]);
                        break;
 
+                 case 'Q':             /* original recipient */
+                       orcpt = newstr(&bp[1]);
+                       break;
+
                  case 'R':             /* specify recipient */
-                       (void) sendtolist(&bp[1], ctladdr, &e->e_sendqueue, e);
+                       p = bp;
+                       qflags = 0;
+                       if (qfver >= 1)
+                       {
+                               /* get flag bits */
+                               while (*++p != '\0' && *p != ':')
+                               {
+                                       switch (*p)
+                                       {
+                                         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;
+                               q->q_flags |= qflags;
+                               q->q_orcpt = orcpt;
+                               (void) recipient(q, &e->e_sendqueue, 0, e);
+                       }
+                       orcpt = NULL;
                        break;
 
                  case 'E':             /* specify error recipient */
-                       (void) sendtolist(&bp[1], ctladdr, &e->e_errorqueue, e);
+                       /* no longer used */
                        break;
 
                  case 'H':             /* header */
-                       (void) chompheader(&bp[1], FALSE, e);
+                       (void) chompheader(&bp[1], FALSE, NULL, e);
                        break;
 
                  case 'M':             /* message */
@@ -1100,26 +1484,39 @@ readqf(e)
                        break;
 
                  case 'D':             /* data file name */
-                       e->e_df = newstr(&bp[1]);
-                       e->e_dfp = fopen(e->e_df, "r");
-                       if (e->e_dfp == NULL)
-                       {
-                               syserr("readqf: cannot open %s", e->e_df);
-                               e->e_msgsize = -1;
-                       }
-                       else if (fstat(fileno(e->e_dfp), &st) >= 0)
-                               e->e_msgsize = st.st_size;
+                       /* obsolete -- ignore */
                        break;
 
                  case 'T':             /* init time */
                        e->e_ctime = atol(&bp[1]);
                        break;
 
+                 case 'I':             /* data file's inode number */
+                       if (e->e_dfino == -1)
+                               e->e_dfino = atol(&buf[1]);
+                       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]);
+                       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)
@@ -1131,22 +1528,31 @@ readqf(e)
                                  case 'r':     /* response */
                                        e->e_flags |= EF_RESPONSE;
                                        break;
+
+                                 case '8':     /* has 8 bit data */
+                                       e->e_flags |= EF_HAS8BIT;
+                                       break;
                                }
                        }
                        break;
 
+                 case 'Z':             /* original envelope id from ESMTP */
+                       e->e_envid = newstr(&bp[1]);
+                       break;
+
                  case '$':             /* define macro */
                        define(bp[1], newstr(&bp[2]), e);
                        break;
 
-                 case '\0':            /* blank line; ignore */
+                 case '.':             /* terminate file */
+                       nomore = TRUE;
                        break;
 
                  default:
                        syserr("readqf: %s: line %d: bad line \"%s\"",
                                qf, LineNumber, bp);
                        fclose(qfp);
-                       rename(qf, queuename(e, 'Q'));
+                       loseqfile(e, "unrecognized line");
                        return FALSE;
                }
 
@@ -1163,7 +1569,49 @@ readqf(e)
        {
                errno = 0;
                e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
+               return TRUE;
+       }
+
+       /* if this has been tried recently, let it be */
+       if (e->e_ntries > 0 && (curtime() - e->e_dtime) < MinQueueAge)
+       {
+               char *howlong = pintvl(curtime() - e->e_dtime, TRUE);
+               extern void unlockqueue();
+
+               if (Verbose || tTd(40, 8))
+                       printf("%s: too young (%s)\n",
+                               e->e_id, howlong);
+#ifdef LOG
+               if (LogLevel > 19)
+                       syslog(LOG_DEBUG, "%s: too young (%s)",
+                               e->e_id, howlong);
+#endif
+               e->e_id = NULL;
+               unlockqueue(e);
+               return FALSE;
+       }
+
+       /*
+       **  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;
+                       e->e_dfdev = st.st_dev;
+                       e->e_dfino = st.st_ino;
+               }
        }
+
        return TRUE;
 }
 \f/*
@@ -1179,6 +1627,7 @@ readqf(e)
 **             Prints a listing of the mail queue on the standard output.
 */
 
+void
 printqueue()
 {
        register WORK *w;
@@ -1193,9 +1642,9 @@ printqueue()
        if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
        {
                struct stat st;
-# ifdef NGROUPS
+# ifdef NGROUPS_MAX
                int n;
-               GIDSET_T gidset[NGROUPS];
+               GIDSET_T gidset[NGROUPS_MAX];
 # endif
 
                if (stat(QueueDir, &st) < 0)
@@ -1203,8 +1652,8 @@ printqueue()
                        syserr("Cannot stat %s", QueueDir);
                        return;
                }
-# ifdef NGROUPS
-               n = getgroups(NGROUPS, gidset);
+# ifdef NGROUPS_MAX
+               n = getgroups(NGROUPS_MAX, gidset);
                while (--n >= 0)
                {
                        if (gidset[n] == st.st_gid)
@@ -1241,8 +1690,8 @@ printqueue()
        CurrentLA = getla();    /* get load average */
 
        printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
-       if (nrequests > QUEUESIZE)
-               printf(", only %d printed", QUEUESIZE);
+       if (nrequests > WorkListSize)
+               printf(", only %d printed", WorkListSize);
        if (Verbose)
                printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
        else
@@ -1251,10 +1700,11 @@ printqueue()
        {
                struct stat st;
                auto time_t submittime = 0;
-               long dfsize = -1;
+               long dfsize;
                int flags = 0;
-               char message[MAXLINE];
-               char bodytype[MAXNAME];
+               int qfver;
+               char statmsg[MAXLINE];
+               char bodytype[MAXNAME + 1];
 
                printf("%8s", w->w_name + 2);
                f = fopen(w->w_name, "r");
@@ -1264,15 +1714,23 @@ printqueue()
                        errno = 0;
                        continue;
                }
-               if (!lockfile(fileno(f), w->w_name, NULL, LOCK_SH|LOCK_NB))
+               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;
 
-               message[0] = bodytype[0] = '\0';
+               statmsg[0] = bodytype[0] = '\0';
+               qfver = 0;
                while (fgets(buf, sizeof buf, f) != NULL)
                {
                        register int i;
@@ -1281,11 +1739,15 @@ printqueue()
                        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 message)
-                                       i = sizeof message - 1;
-                               bcopy(&buf[1], message, i);
-                               message[i] = '\0';
+                               if ((i = strlen(&buf[1])) >= sizeof statmsg)
+                                       i = sizeof statmsg - 1;
+                               bcopy(&buf[1], statmsg, i);
+                               statmsg[i] = '\0';
                                break;
 
                          case 'B':     /* body type */
@@ -1306,11 +1768,11 @@ printqueue()
                                else
                                        printf("%8ld %.16s %.45s", dfsize,
                                            ctime(&submittime), &buf[1]);
-                               if (message[0] != '\0' || bodytype[0] != '\0')
+                               if (statmsg[0] != '\0' || bodytype[0] != '\0')
                                {
                                        printf("\n    %10.10s", bodytype);
-                                       if (message[0] != '\0')
-                                               printf("   (%.60s)", message);
+                                       if (statmsg[0] != '\0')
+                                               printf("   (%.60s)", statmsg);
                                }
                                break;
 
@@ -1321,21 +1783,24 @@ printqueue()
                                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  %.38s", &buf[1]);
+                                       printf("\n\t\t\t\t\t  %.38s", p);
                                else
-                                       printf("\n\t\t\t\t   %.45s", &buf[1]);
+                                       printf("\n\t\t\t\t   %.45s", p);
                                break;
 
                          case 'T':     /* creation time */
                                submittime = atol(&buf[1]);
                                break;
 
-                         case 'D':     /* data file name */
-                               if (stat(&buf[1], &st) >= 0)
-                                       dfsize = st.st_size;
-                               break;
-
                          case 'F':     /* flag bits */
                                for (p = &buf[1]; *p != '\0'; p++)
                                {
@@ -1388,7 +1853,7 @@ queuename(e, type)
        static char c2;
        time_t now;
        struct tm *tm;
-       static char buf[MAXNAME];
+       static char buf[MAXNAME + 1];
 
        if (e->e_id == NULL)
        {
@@ -1480,6 +1945,7 @@ queuename(e, type)
 **             unlocks the queue for `e'.
 */
 
+void
 unlockqueue(e)
        ENVELOPE *e;
 {
@@ -1545,7 +2011,7 @@ setctluser(user)
        p = strchr(user, ':');
        if (p != NULL)
                *p++ = '\0';
-       if (*user != '\0' && (pw = getpwnam(user)) != NULL)
+       if (*user != '\0' && (pw = sm_getpwnam(user)) != NULL)
        {
                if (strcmp(pw->pw_dir, "/") == 0)
                        a->q_home = "";
@@ -1553,13 +2019,15 @@ setctluser(user)
                        a->q_home = newstr(pw->pw_dir);
                a->q_uid = pw->pw_uid;
                a->q_gid = pw->pw_gid;
-               a->q_user = newstr(user);
                a->q_flags |= QGOODUID;
        }
+
+       if (*user != '\0')
+               a->q_user = newstr(user);
+       else if (p != NULL)
+               a->q_user = newstr(p);
        else
-       {
                a->q_user = newstr(DefUser);
-       }
 
        a->q_flags |= QPRIMARY;         /* flag as a "ctladdr"  */
        a->q_mailer = LocalMailer;
@@ -1569,3 +2037,35 @@ setctluser(user)
                a->q_paddr = newstr(p);
        return a;
 }
+\f/*
+**  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[40];
+
+       if (e == NULL || e->e_id == NULL)
+               return;
+       if (strlen(e->e_id) > sizeof buf - 4)
+               return;
+       strcpy(buf, queuename(e, 'q'));
+       p = queuename(e, 'Q');
+       if (rename(buf, p) < 0)
+               syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid());
+#ifdef LOG
+       else if (LogLevel > 0)
+               syslog(LOG_ALERT, "Losing %s: %s", buf, why);
+#endif
+}
index ad7da2a..e752865 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)readcf.c   8.23.1.3 (Berkeley) 3/5/95";
+static char sccsid[] = "@(#)readcf.c   8.139 (Berkeley) 11/29/95";
 #endif /* not lint */
 
 # include "sendmail.h"
-# include <pwd.h>
 # include <grp.h>
 #if NAMED_BIND
-# include <arpa/nameser.h>
 # include <resolv.h>
 #endif
 
@@ -76,6 +74,7 @@ static char sccsid[] = "@(#)readcf.c  8.23.1.3 (Berkeley) 3/5/95";
 **             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 -- control file name.
@@ -90,6 +89,7 @@ static char sccsid[] = "@(#)readcf.c  8.23.1.3 (Berkeley) 3/5/95";
 **             Builds several internal tables.
 */
 
+void
 readcf(cfname, safe, e)
        char *cfname;
        bool safe;
@@ -104,14 +104,18 @@ readcf(cfname, safe, e)
        int nfuzzy;
        char *file;
        bool optional;
+       int mid;
        char buf[MAXLINE];
        register char *p;
        extern char **copyplist();
        struct stat statb;
        char exbuf[MAXLINE];
        char pvpbuf[MAXLINE + MAXATOM];
-       extern char *munchstring();
-       extern void makemapentry();
+       static char *null_list[1] = { NULL };
+       extern char *munchstring __P((char *, char **));
+       extern void fileclass __P((int, char *, char *, bool, bool));
+       extern void toomany __P((int, int));
+       extern void translate_dollars __P((char *));
 
        FileName = cfname;
        LineNumber = 0;
@@ -137,7 +141,7 @@ readcf(cfname, safe, e)
 
        if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
        {
-               if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
+               if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
                        fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
                                FileName);
 #ifdef LOG
@@ -160,52 +164,8 @@ readcf(cfname, safe, e)
                        continue;
                }
 
-               /* map $ into \201 for macro expansion */
-               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 > bp)
-                                               p--;
-                                       if ((e = strchr(++p, '\n')) != NULL)
-                                               (void) strcpy(p, e);
-                                       else
-                                               p[0] = p[1] = '\0';
-                                       break;
-                               }
-                               continue;
-                       }
-
-                       if (*p != '$')
-                               continue;
-
-                       if (p[1] == '$')
-                       {
-                               /* actual dollar sign.... */
-                               (void) strcpy(p, p + 1);
-                               continue;
-                       }
-
-                       /* convert to macro expansion character */
-                       *p = MACROEXPAND;
-               }
+               /* do macro expansion mappings */
+               translate_dollars(bp);
 
                /* interpret this line */
                errno = 0;
@@ -240,9 +200,9 @@ readcf(cfname, safe, e)
 
                        /* expand and save the LHS */
                        *p = '\0';
-                       expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
+                       expand(&bp[1], exbuf, sizeof exbuf, e);
                        rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
-                                            sizeof pvpbuf, NULL);
+                                            sizeof pvpbuf, NULL, NULL);
                        nfuzzy = 0;
                        if (rwp->r_lhs != NULL)
                        {
@@ -316,7 +276,10 @@ readcf(cfname, safe, e)
                                }
                        }
                        else
+                       {
                                syserr("R line: null LHS");
+                               rwp->r_lhs = null_list;
+                       }
 
                        /* expand and save the RHS */
                        while (*++p == '\t')
@@ -325,9 +288,9 @@ readcf(cfname, safe, e)
                        while (*p != '\0' && *p != '\t')
                                p++;
                        *p = '\0';
-                       expand(q, exbuf, &exbuf[sizeof exbuf], e);
+                       expand(q, exbuf, sizeof exbuf, e);
                        rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
-                                            sizeof pvpbuf, NULL);
+                                            sizeof pvpbuf, NULL, NULL);
                        if (rwp->r_rhs != NULL)
                        {
                                register char **ap;
@@ -377,40 +340,51 @@ readcf(cfname, safe, e)
                                }
                        }
                        else
+                       {
                                syserr("R line: null RHS");
+                               rwp->r_rhs = null_list;
+                       }
                        break;
 
                  case 'S':             /* select rewriting set */
-                       for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
-                               continue;
-                       if (!isascii(*p) || !isdigit(*p))
-                       {
-                               syserr("invalid argument to S line: \"%.20s\"", 
-                                       &bp[1]);
+                       expand(&bp[1], exbuf, sizeof exbuf, e);
+                       ruleset = strtorwset(exbuf, NULL, ST_ENTER);
+                       if (ruleset < 0)
                                break;
-                       }
-                       ruleset = atoi(p);
-                       if (ruleset >= MAXRWSETS || ruleset < 0)
+                       rwp = RewriteRules[ruleset];
+                       if (rwp != NULL)
                        {
-                               syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
-                               ruleset = 0;
+                               while (rwp->r_next != NULL)
+                                       rwp = rwp->r_next;
+                               fprintf(stderr, "WARNING: Ruleset %s redefined\n",
+                                       &bp[1]);
                        }
-                       rwp = NULL;
                        break;
 
                  case 'D':             /* macro definition */
-                       p = munchstring(&bp[2], NULL);
-                       define(bp[1], newstr(p), e);
+                       mid = macid(&bp[1], &ep);
+                       p = munchstring(ep, NULL);
+                       define(mid, newstr(p), e);
                        break;
 
                  case 'H':             /* required header line */
-                       (void) chompheader(&bp[1], TRUE, e);
+                       (void) chompheader(&bp[1], TRUE, NULL, e);
                        break;
 
                  case 'C':             /* word class */
-                       /* scan the list of words and set class for all */
-                       expand(&bp[2], exbuf, &exbuf[sizeof exbuf], e);
-                       for (p = exbuf; *p != '\0'; )
+                 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;
@@ -423,13 +397,14 @@ readcf(cfname, safe, e)
                                delim = *p;
                                *p = '\0';
                                if (wd[0] != '\0')
-                                       setclass(bp[1], wd);
+                                       setclass(mid, wd);
                                *p = delim;
                        }
                        break;
 
                  case 'F':             /* word class from file */
-                       for (p = &bp[2]; isascii(*p) && isspace(*p); )
+                       mid = macid(&bp[1], &ep);
+                       for (p = ep; isascii(*p) && isspace(*p); )
                                p++;
                        if (p[0] == '-' && p[1] == 'o')
                        {
@@ -437,22 +412,27 @@ readcf(cfname, safe, e)
                                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
                                        p++;
                                while (isascii(*p) && isspace(*p))
-                                       *p++;
+                                       p++;
                        }
                        else
                                optional = FALSE;
                        file = p;
-                       while (*p != '\0' && !(isascii(*p) && isspace(*p)))
-                               p++;
-                       if (*p == '\0')
+                       if (*file == '|')
                                p = "%s";
                        else
                        {
-                               *p = '\0';
-                               while (isascii(*++p) && isspace(*p))
-                                       continue;
+                               while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+                                       p++;
+                               if (*p == '\0')
+                                       p = "%s";
+                               else
+                               {
+                                       *p = '\0';
+                                       while (isascii(*++p) && isspace(*p))
+                                               continue;
+                               }
                        }
-                       fileclass(bp[1], file, p, safe, optional);
+                       fileclass(mid, file, p, safe, optional);
                        break;
 
 #ifdef XLA
@@ -475,7 +455,7 @@ readcf(cfname, safe, e)
                                toomany('P', MAXPRIORITIES);
                                break;
                        }
-                       for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
+                       for (p = &bp[1]; *p != '\0' && *p != '='; p++)
                                continue;
                        if (*p == '\0')
                                goto badline;
@@ -485,10 +465,6 @@ readcf(cfname, safe, e)
                        NumPriorities++;
                        break;
 
-                 case 'T':             /* trusted user(s) */
-                       /* this option is obsolete, but will be ignored */
-                       break;
-
                  case 'V':             /* configuration syntax version */
                        for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
                                continue;
@@ -499,13 +475,28 @@ readcf(cfname, safe, e)
                                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++ == '/')
                        {
                                /* extract vendor code */
@@ -520,7 +511,15 @@ readcf(cfname, safe, e)
                        break;
 
                  case 'K':
-                       makemapentry(&bp[1]);
+                       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:
@@ -532,24 +531,133 @@ readcf(cfname, safe, e)
        }
        if (ferror(cf))
        {
-               syserr("I/O read error", cfname);
+               syserr("I/O read error");
                exit(EX_OSFILE);
        }
        fclose(cf);
        FileName = NULL;
 
-       if (stab("host", ST_MAP, ST_FIND) == NULL)
+       /* initialize host maps from local service tables */
+       inithostmaps();
+
+       /* determine if we need to do special name-server frotz */
        {
-               /* user didn't initialize: set up host map */
-               strcpy(buf, "host host");
-#if NAMED_BIND
-               if (ConfigLevel >= 2)
-                       strcat(buf, " -a.");
+               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
-               makemapentry(buf);
        }
 }
 \f/*
+**  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';
+}
+\f/*
 **  TOOMANY -- signal too many of some option
 **
 **     Parameters:
@@ -563,8 +671,9 @@ readcf(cfname, safe, e)
 **             gives a syserr.
 */
 
+void
 toomany(id, maxcnt)
-       char id;
+       int id;
        int maxcnt;
 {
        syserr("too many %c lines, %d max", id, maxcnt);
@@ -589,6 +698,7 @@ toomany(id, maxcnt)
 **                     the named class.
 */
 
+void
 fileclass(class, filename, fmt, safe, optional)
        int class;
        char *filename;
@@ -597,7 +707,9 @@ fileclass(class, filename, fmt, safe, optional)
        bool optional;
 {
        FILE *f;
-       struct stat stbuf;
+       int sff;
+       int pid;
+       register char *p;
        char buf[MAXLINE];
 
        if (tTd(37, 2))
@@ -605,42 +717,49 @@ fileclass(class, filename, fmt, safe, optional)
 
        if (filename[0] == '|')
        {
-               syserr("fileclass: pipes (F%c%s) not supported due to security problems",
-                       class, filename);
-               return;
-       }
-       if (stat(filename, &stbuf) < 0)
-       {
-               if (tTd(37, 2))
-                       printf("  cannot stat (%s)\n", errstring(errno));
-               if (!optional)
-                       syserr("fileclass: cannot stat %s", filename);
-               return;
-       }
-       if (!S_ISREG(stbuf.st_mode))
-       {
-               syserr("fileclass: %s not a regular file", filename);
-               return;
+               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");
        }
-       if (!safe && access(filename, R_OK) < 0)
+       else
        {
-               syserr("fileclass: access denied on %s", filename);
-               return;
+               pid = -1;
+               sff = SFF_REGONLY;
+               if (safe)
+                       sff |= SFF_OPENASROOT;
+               f = safefopen(filename, O_RDONLY, 0, sff);
        }
-       f = fopen(filename, "r");
        if (f == NULL)
        {
-               syserr("fileclass: cannot open %s", filename);
+               if (!optional)
+                       syserr("fileclass: cannot open %s", filename);
                return;
        }
 
        while (fgets(buf, sizeof buf, f) != NULL)
        {
-               register STAB *s;
                register char *p;
-# ifdef SCANF
+# if SCANF
                char wordbuf[MAXNAME+1];
+# endif
 
+               if (buf[0] == '#')
+                       continue;
+# if SCANF
                if (sscanf(buf, fmt, wordbuf) != 1)
                        continue;
                p = wordbuf;
@@ -675,6 +794,8 @@ fileclass(class, filename, fmt, safe, optional)
        }
 
        (void) fclose(f);
+       if (pid > 0)
+               (void) waitfor(pid);
 }
 \f/*
 **  MAKEMAILER -- define a new mailer.
@@ -682,12 +803,19 @@ fileclass(class, filename, fmt, safe, optional)
 **     Parameters:
 **             line -- description of mailer.  This is in labeled
 **                     fields.  The fields are:
-**                        P -- the path to the mailer
-**                        F -- the flags associated with the mailer
 **                        A -- the argv for this mailer
-**                        S -- the sender rewriting set
-**                        R -- the recipient rewriting set
+**                        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:
@@ -697,6 +825,7 @@ fileclass(class, filename, fmt, safe, optional)
 **             enters the mailer into the mailer table.
 */
 
+void
 makemailer(line)
        char *line;
 {
@@ -709,18 +838,20 @@ makemailer(line)
        extern int NextMailer;
        extern char **makeargv();
        extern char *munchstring();
-       extern long atol();
 
        /* allocate a mailer and set up defaults */
        m = (struct mailer *) xalloc(sizeof *m);
        bzero((char *) m, sizeof *m);
        m->m_eol = "\n";
+       m->m_uid = m->m_gid = 0;
 
        /* 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 */
@@ -750,6 +881,8 @@ makemailer(line)
                switch (fcode)
                {
                  case 'P':             /* pathname */
+                       if (*p == '\0')
+                               syserr("mailer %s: empty path name", m->m_name);
                        m->m_mailer = newstr(p);
                        break;
 
@@ -761,12 +894,9 @@ makemailer(line)
 
                  case 'S':             /* sender rewriting ruleset */
                  case 'R':             /* recipient rewriting ruleset */
-                       i = strtol(p, &endp, 10);
-                       if (i < 0 || i >= MAXRWSETS)
-                       {
-                               syserr("invalid rewrite set, %d max", MAXRWSETS);
+                       i = strtorwset(p, &endp, ST_ENTER);
+                       if (i < 0)
                                return;
-                       }
                        if (fcode == 'S')
                                m->m_sh_rwset = m->m_se_rwset = i;
                        else
@@ -775,13 +905,9 @@ makemailer(line)
                        p = endp;
                        if (*p++ == '/')
                        {
-                               i = strtol(p, NULL, 10);
-                               if (i < 0 || i >= MAXRWSETS)
-                               {
-                                       syserr("invalid rewrite set, %d max",
-                                               MAXRWSETS);
+                               i = strtorwset(p, NULL, ST_ENTER);
+                               if (i < 0)
                                        return;
-                               }
                                if (fcode == 'S')
                                        m->m_sh_rwset = i;
                                else
@@ -790,10 +916,16 @@ makemailer(line)
                        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;
 
@@ -803,25 +935,122 @@ makemailer(line)
 
                  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')
+                               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 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);
-       }
-
        /* do some rationality checking */
        if (m->m_argv == NULL)
        {
@@ -840,6 +1069,27 @@ makemailer(line)
                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";
+       }
+
+       /* enter the mailer into the symbol table */
        s = stab(m->m_name, ST_MAILER, ST_ENTER);
        if (s->s_mailer != NULL)
        {
@@ -875,7 +1125,7 @@ munchstring(p, delimptr)
        bool quotemode = FALSE;
        static char buf[MAXLINE];
 
-       for (q = buf; *p != '\0'; p++)
+       for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
        {
                if (backslash)
                {
@@ -973,6 +1223,7 @@ makeargv(p)
 **             prints rewrite rules.
 */
 
+void
 printrules()
 {
        register struct rewrite *rwp;
@@ -993,7 +1244,52 @@ printrules()
                }
        }
 }
+\f/*
+**  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,
+               m->m_uid, 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");
+}
 \f/*
 **  SETOPTION -- set global processing option
 **
@@ -1015,6 +1311,7 @@ printrules()
 */
 
 static BITMAP  StickyOpt;              /* set if option is stuck */
+extern void    settimeout __P((char *, char *));
 
 
 #if NAMED_BIND
@@ -1040,23 +1337,222 @@ struct resolverflags
 
 #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',            FALSE,
+       "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',            TRUE,
+       "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',            TRUE,
+       "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_MHSA         0x84
+/*
+       "MaxHostStatAge",       O_MHSA,         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, TRUE,
+#define O_SLFH         0x94
+#ifdef LOTUS_NOTES_HACK
+       "SingleLineFromHeader", O_SLFH,         TRUE,
+#endif
+
+       NULL,                   '\0',           FALSE,
+};
+
+
+
+void
 setoption(opt, val, safe, sticky, e)
-       char opt;
+       int opt;
        char *val;
        bool safe;
        bool sticky;
        register ENVELOPE *e;
 {
        register char *p;
+       register struct optioninfo *o;
+       char *subopt;
+       char buf[50];
        extern bool atobool();
        extern time_t convtime();
        extern int QueueLA;
        extern int RefuseLA;
        extern bool Warn_Q_option;
-       extern bool trusteduser();
+
+       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))
+               {
+                       bool oldVerbose = Verbose;
+
+                       Verbose = TRUE;
+                       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("setoption %c=%s", opt, val);
+       {
+               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.
@@ -1075,7 +1571,7 @@ setoption(opt, val, safe, sticky, e)
 
        if (!safe && RealUid == 0)
                safe = TRUE;
-       if (!safe && strchr("bCdeijLmoprsvw7", opt) == NULL)
+       if (!safe && !o->o_safe)
        {
                if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
                {
@@ -1085,6 +1581,7 @@ setoption(opt, val, safe, sticky, e)
                        {
                                if (tTd(37, 1))
                                        printf("(Resetting uid)");
+                               endpwent();
                                (void) setgid(RealGid);
                                (void) setuid(RealUid);
                        }
@@ -1093,12 +1590,53 @@ setoption(opt, val, safe, sticky, e)
        if (tTd(37, 1))
                printf("\n");
 
-       switch (opt)
+       switch (opt & 0xff)
        {
          case '7':             /* force seven-bit input */
-               SevenBit = atobool(val);
+               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");
@@ -1145,8 +1683,9 @@ setoption(opt, val, safe, sticky, e)
                        break;
 
                  case SM_QUEUE:        /* queue only */
+                 case SM_DEFER:        /* queue only and defer map lookups */
 #ifndef QUEUE
-                       syserr("need QUEUE to set -odqueue");
+                       syserr("need QUEUE to set -odqueue or -oddefer");
 #endif /* QUEUE */
                        /* fall through..... */
 
@@ -1177,9 +1716,6 @@ setoption(opt, val, safe, sticky, e)
                  case EM_MAIL:         /* mail back */
                  case EM_BERKNET:      /* do berknet error processing */
                  case EM_WRITE:        /* write back (or mail) */
-                       HoldErrs = TRUE;
-                       /* fall through... */
-
                  case EM_PRINT:        /* print errors normally (default) */
                        e->e_errormode = *val;
                        break;
@@ -1199,6 +1735,7 @@ setoption(opt, val, safe, sticky, e)
                break;
 
          case 'g':             /* default gid */
+  g_opt:
                if (isascii(*val) && isdigit(*val))
                        DefGid = atoi(val);
                else
@@ -1208,7 +1745,8 @@ setoption(opt, val, safe, sticky, e)
                        DefGid = -1;
                        gr = getgrnam(val);
                        if (gr == NULL)
-                               syserr("readcf: option g: unknown group %s", val);
+                               syserr("readcf: option %c: unknown group %s",
+                                       opt, val);
                        else
                                DefGid = gr->gr_gid;
                }
@@ -1227,7 +1765,6 @@ setoption(opt, val, safe, sticky, e)
 
          case 'I':             /* use internet domain name server */
 #if NAMED_BIND
-               UseNameServer = TRUE;
                for (p = val; *p != 0; )
                {
                        bool clearmode;
@@ -1249,6 +1786,11 @@ setoption(opt, val, safe, sticky, e)
                                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)
@@ -1262,7 +1804,8 @@ setoption(opt, val, safe, sticky, e)
                                _res.options |= rfp->rf_bits;
                }
                if (tTd(8, 2))
-                       printf("_res.options = %x\n", _res.options);
+                       printf("_res.options = %x, HasWildcardMX = %d\n",
+                               _res.options, HasWildcardMX);
 #else
                usrerr("name server (I option) specified but BIND not compiled in");
 #endif
@@ -1318,7 +1861,11 @@ setoption(opt, val, safe, sticky, e)
            /* 'N' available -- was "net name" */
 
          case 'O':             /* daemon options */
+#ifdef DAEMON
                setdaemonoptions(val);
+#else
+               syserr("DaemonPortOptions (O option) set but DAEMON not compiled in");
+#endif
                break;
 
          case 'o':             /* assume old style headers */
@@ -1379,7 +1926,10 @@ setoption(opt, val, safe, sticky, e)
                break;
 
          case 'r':             /* read timeout */
-               settimeouts(val);
+               if (subopt == NULL)
+                       inittimeouts(val);
+               else
+                       settimeout(subopt, val);
                break;
 
          case 'S':             /* status file */
@@ -1398,9 +1948,9 @@ setoption(opt, val, safe, sticky, e)
                if (p != NULL)
                {
                        *p++ = '\0';
-                       TimeOuts.to_q_warning = convtime(p, 'd');
+                       settimeout("queuewarn", p);
                }
-               TimeOuts.to_q_return = convtime(val, 'h');
+               settimeout("queuereturn", val);
                break;
 
          case 't':             /* time zone name */
@@ -1412,6 +1962,14 @@ setoption(opt, val, safe, sticky, e)
                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);
                else
@@ -1419,14 +1977,22 @@ setoption(opt, val, safe, sticky, e)
                        register struct passwd *pw;
 
                        DefUid = -1;
-                       pw = getpwnam(val);
+                       pw = sm_getpwnam(val);
                        if (pw == NULL)
                                syserr("readcf: option u: unknown user %s", val);
                        else
+                       {
                                DefUid = pw->pw_uid;
+                               DefGid = pw->pw_gid;
+                       }
                }
                setdefuser();
-               break;
+
+               /* handle the group if it is there */
+               if (*p == '\0')
+                       break;
+               val = p;
+               goto g_opt;
 
          case 'V':             /* fallback MX host */
                FallBackMX = newstr(val);
@@ -1466,19 +2032,133 @@ setoption(opt, val, safe, sticky, e)
                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;
+
+                 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_MHSA:          /* maximum age of cached host status */
+               MaxHostStatAge = 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) < sizeof buf - 10)
+                       strcat(buf, val);
+               MustQuoteChars = newstr(buf);
+               break;
+
+         case O_SMTPGREETING:  /* SMTP greeting message (old $e macro) */
+               SmtpGreeting = newstr(munchstring(val, NULL));
+               break;
+
+         case O_UNIXFROM:      /* UNIX From_ line (old $l macro) */
+               UnixFromLine = newstr(munchstring(val, NULL));
+               break;
+
+         case O_OPCHARS:       /* operator characters (old $o macro) */
+               OperatorChars = newstr(munchstring(val, NULL));
+               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;
+
          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);
-       return;
 }
 \f/*
-**  SETCLASS -- set a word into a class
+**  SETCLASS -- set a string into a class
 **
 **     Parameters:
-**             class -- the class to put the word in.
-**             word -- the word to enter
+**             class -- the class to put the string in.
+**             str -- the string to enter
 **
 **     Returns:
 **             none.
@@ -1487,15 +2167,16 @@ setoption(opt, val, safe, sticky, e)
 **             puts the word into the symbol table.
 */
 
-setclass(class, word)
+void
+setclass(class, str)
        int class;
-       char *word;
+       char *str;
 {
        register STAB *s;
 
        if (tTd(37, 8))
-               printf("setclass(%c, %s)\n", class, word);
-       s = stab(word, ST_CLASS, ST_ENTER);
+               printf("setclass(%s, %s)\n", macname(class), str);
+       s = stab(str, ST_CLASS, ST_ENTER);
        setbitn(class, s->s_class);
 }
 \f/*
@@ -1505,14 +2186,14 @@ setclass(class, word)
 **             line -- the config file line
 **
 **     Returns:
-**             TRUE if it successfully entered the map entry.
-**             FALSE otherwise (usually syntax error).
+**             A pointer to the map that has been created.
+**             NULL if there was a syntax error.
 **
 **     Side Effects:
 **             Enters the map into the dictionary.
 */
 
-void
+MAP *
 makemapentry(line)
        char *line;
 {
@@ -1527,11 +2208,11 @@ makemapentry(line)
        if (!(isascii(*p) && isalnum(*p)))
        {
                syserr("readcf: config K line: no map name");
-               return;
+               return NULL;
        }
 
        mapname = p;
-       while (isascii(*++p) && isalnum(*p))
+       while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
                continue;
        if (*p != '\0')
                *p++ = '\0';
@@ -1540,7 +2221,7 @@ makemapentry(line)
        if (!(isascii(*p) && isalnum(*p)))
        {
                syserr("readcf: config K line, map %s: no map class", mapname);
-               return;
+               return NULL;
        }
        classname = p;
        while (isascii(*++p) && isalnum(*p))
@@ -1555,7 +2236,7 @@ makemapentry(line)
        if (class == NULL)
        {
                syserr("readcf: map %s: class %s not available", mapname, classname);
-               return;
+               return NULL;
        }
 
        /* enter the map */
@@ -1577,9 +2258,128 @@ makemapentry(line)
                        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;
 }
 \f/*
-**  SETTIMEOUTS -- parse and set timeout values
+**  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 || !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)
+               {
+                       syserr("unknown ruleset %s", q);
+                       return -1;
+               }
+
+               if (stabmode == ST_ENTER && delim == '=')
+               {
+                       while (isascii(*++p) && isspace(*p))
+                               continue;
+                       if (!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, ruleset, s->s_ruleset);
+                       ruleset = s->s_ruleset;
+               }
+               else if (ruleset > 0)
+               {
+                       s->s_ruleset = ruleset;
+               }
+       }
+       return ruleset;
+}
+\f/*
+**  INITTIMEOUTS -- parse and set timeout values
 **
 **     Parameters:
 **             val -- a pointer to the values.  If NULL, do initial
@@ -1596,7 +2396,8 @@ makemapentry(line)
 #define MINUTES        * 60
 #define HOUR   * 3600
 
-settimeouts(val)
+void
+inittimeouts(val)
        register char *val;
 {
        register char *p;
@@ -1604,6 +2405,7 @@ settimeouts(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;
@@ -1615,7 +2417,12 @@ settimeouts(val)
                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;
                return;
        }
 
@@ -1643,43 +2450,107 @@ settimeouts(val)
                }
                else
                {
-                       register char *q = strchr(val, '=');
-                       time_t to;
+                       register char *q = strchr(val, ':');
 
-                       if (q == NULL)
+                       if (q == NULL && (q = strchr(val, '=')) == NULL)
                        {
                                /* syntax error */
                                continue;
                        }
                        *q++ = '\0';
-                       to = convtime(q, 'm');
-
-                       if (strcasecmp(val, "initial") == 0)
-                               TimeOuts.to_initial = to;
-                       else if (strcasecmp(val, "mail") == 0)
-                               TimeOuts.to_mail = to;
-                       else if (strcasecmp(val, "rcpt") == 0)
-                               TimeOuts.to_rcpt = to;
-                       else if (strcasecmp(val, "datainit") == 0)
-                               TimeOuts.to_datainit = to;
-                       else if (strcasecmp(val, "datablock") == 0)
-                               TimeOuts.to_datablock = to;
-                       else if (strcasecmp(val, "datafinal") == 0)
-                               TimeOuts.to_datafinal = to;
-                       else if (strcasecmp(val, "command") == 0)
-                               TimeOuts.to_nextcommand = to;
-                       else if (strcasecmp(val, "rset") == 0)
-                               TimeOuts.to_rset = to;
-                       else if (strcasecmp(val, "helo") == 0)
-                               TimeOuts.to_helo = to;
-                       else if (strcasecmp(val, "quit") == 0)
-                               TimeOuts.to_quit = to;
-                       else if (strcasecmp(val, "misc") == 0)
-                               TimeOuts.to_miscshort = to;
-                       else if (strcasecmp(val, "ident") == 0)
-                               TimeOuts.to_ident = to;
-                       else
-                               syserr("settimeouts: invalid timeout %s", val);
+                       settimeout(val, q);
                }
        }
 }
+\f/*
+**  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();
+
+       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, "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
+               syserr("settimeout: invalid timeout %s", name);
+}
index a43cf5c..79126e9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)recipient.c        8.44.1.6 (Berkeley) 3/5/95";
+static char sccsid[] = "@(#)recipient.c        8.108 (Berkeley) 10/30/95";
 #endif /* not lint */
 
 # include "sendmail.h"
-# include <pwd.h>
 
 /*
 **  SENDTOLIST -- Designate a send list.
@@ -53,6 +52,8 @@ static char sccsid[] = "@(#)recipient.c       8.44.1.6 (Berkeley) 3/5/95";
 **                     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:
@@ -62,12 +63,15 @@ static char sccsid[] = "@(#)recipient.c     8.44.1.6 (Berkeley) 3/5/95";
 **             none.
 */
 
-# define MAXRCRSN      10
+/* q_flags bits inherited from ctladdr */
+#define QINHERITEDBITS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY)
 
-sendtolist(list, ctladdr, sendq, e)
+int
+sendtolist(list, ctladdr, sendq, aliaslevel, e)
        char *list;
        ADDRESS *ctladdr;
        ADDRESS **sendq;
+       int aliaslevel;
        register ENVELOPE *e;
 {
        register char *p;
@@ -128,13 +132,58 @@ sendtolist(list, ctladdr, sendq, e)
                a->q_next = al;
                a->q_alias = ctladdr;
 
-               /* see if this should be marked as a primary address */
-               if (ctladdr == NULL ||
-                   (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags)))
-                       a->q_flags |= QPRIMARY;
+               /* arrange to inherit attributes from parent */
+               if (ctladdr != NULL)
+               {
+                       ADDRESS *b;
+                       extern ADDRESS *self_reference();
+
+                       /* 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;
+               }
 
-               if (ctladdr != NULL && sameaddr(ctladdr, a))
-                       ctladdr->q_flags |= QSELFREF;
                al = a;
                firstone = FALSE;
        }
@@ -145,11 +194,7 @@ sendtolist(list, ctladdr, sendq, e)
                register ADDRESS *a = al;
 
                al = a->q_next;
-               a = recipient(a, sendq, e);
-
-               /* arrange to inherit full name */
-               if (a->q_fullname == NULL && ctladdr != NULL)
-                       a->q_fullname = ctladdr->q_fullname;
+               a = recipient(a, sendq, aliaslevel, e);
                naddrs++;
        }
 
@@ -168,6 +213,7 @@ sendtolist(list, ctladdr, sendq, e)
 **             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:
@@ -179,9 +225,10 @@ sendtolist(list, ctladdr, sendq, e)
 */
 
 ADDRESS *
-recipient(a, sendq, e)
+recipient(a, sendq, aliaslevel, e)
        register ADDRESS *a;
        register ADDRESS **sendq;
+       int aliaslevel;
        register ENVELOPE *e;
 {
        register ADDRESS *q;
@@ -190,15 +237,20 @@ recipient(a, sendq, e)
        register char *p;
        bool quoted = FALSE;            /* set if the addr has a quote bit */
        int findusercount = 0;
-       char buf[MAXNAME];              /* unquoted image of the user name */
+       bool initialdontsend = bitset(QDONTSEND, a->q_flags);
+       int i;
+       char *buf;
+       char buf0[MAXNAME + 1];         /* unquoted image of the user name */
        extern int safefile();
 
        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: ");
+               printf("\nrecipient (%d): ", aliaslevel);
                printaddr(a, FALSE);
        }
 
@@ -212,9 +264,11 @@ recipient(a, sendq, e)
        }
 
        /* break aliasing loops */
-       if (AliasLevel > MAXRCRSN)
+       if (aliaslevel > MaxAliasRecursion)
        {
-               usrerr("554 aliasing/forwarding loop broken");
+               a->q_status = "5.4.6";
+               usrerr("554 aliasing/forwarding loop broken (%d aliases deep; %d max",
+                       aliaslevel, MaxAliasRecursion);
                return (a);
        }
 
@@ -222,10 +276,12 @@ recipient(a, sendq, e)
        **  Finish setting up address structure.
        */
 
-       /* set the queue timeout */
-       a->q_timeout = TimeOuts.to_q_return;
-
        /* 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++)
        {
@@ -240,17 +296,20 @@ recipient(a, sendq, e)
                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";
                        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";
                        usrerr("550 Address %s is unsafe for mailing to programs",
                                a->q_alias->q_paddr);
                }
@@ -283,7 +342,7 @@ recipient(a, sendq, e)
                        else if (bitset(QSELFREF, q->q_flags))
                                q->q_flags |= a->q_flags & ~QDONTSEND;
                        a = q;
-                       goto testselfdestruct;
+                       goto done;
                }
        }
 
@@ -308,6 +367,7 @@ recipient(a, sendq, e)
                if (a->q_alias == NULL)
                {
                        a->q_flags |= QBADADDR;
+                       a->q_status = "5.7.1";
                        usrerr("550 Cannot mail directly to :include:s");
                }
                else
@@ -315,25 +375,29 @@ recipient(a, sendq, e)
                        int ret;
 
                        message("including file %s", a->q_user);
-                       ret = include(a->q_user, FALSE, a, sendq, e);
+                       ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e);
                        if (transienterror(ret))
                        {
 #ifdef LOG
                                if (LogLevel > 2)
                                        syslog(LOG_ERR, "%s: include %s: transient error: %s",
                                                e->e_id == NULL ? "NOQUEUE" : e->e_id,
-                                               a->q_user, errstring(ret));
+                                               shortenstring(a->q_user, 203),
+                                               errstring(ret));
 #endif
                                a->q_flags |= QQUEUEUP;
                                a->q_flags &= ~QDONTSEND;
                                usrerr("451 Cannot open %s: %s",
-                                       a->q_user, errstring(ret));
+                                       shortenstring(a->q_user, 203),
+                                       errstring(ret));
                        }
                        else if (ret != 0)
                        {
                                a->q_flags |= QBADADDR;
+                               a->q_status = "5.2.4";
                                usrerr("550 Cannot open %s: %s",
-                                       a->q_user, errstring(ret));
+                                       shortenstring(a->q_user, 203),
+                                       errstring(ret));
                        }
                }
        }
@@ -345,44 +409,44 @@ recipient(a, sendq, e)
                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";
                        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";
                        usrerr("550 Address %s is unsafe for mailing to files",
                                a->q_alias->q_paddr);
                }
-               else if (!writable(buf, getctladdr(a), SFF_ANYFILE))
+               else if (!writable(buf, a->q_alias, SFF_CREAT))
                {
                        a->q_flags |= QBADADDR;
-                       giveresponse(EX_CANTCREAT, m, NULL, a->q_alias, e);
+                       giveresponse(EX_CANTCREAT, m, NULL, a->q_alias,
+                                    (time_t) 0, e);
                }
        }
 
-       if (m != LocalMailer)
-       {
-               if (!bitset(QDONTSEND, a->q_flags))
-                       e->e_nrcpts++;
-               goto testselfdestruct;
-       }
-
        /* try aliasing */
-       alias(a, sendq, e);
+       if (!quoted && !bitset(QDONTSEND, a->q_flags) &&
+           bitnset(M_ALIASABLE, m->m_flags))
+               alias(a, sendq, aliaslevel, e);
 
-# ifdef USERDB
+# if USERDB
        /* if not aliased, look it up in the user database */
-       if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags))
+       if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags) &&
+           bitnset(M_CHECKUDB, m->m_flags))
        {
                extern int udbexpand();
 
-               if (udbexpand(a, sendq, e) == EX_TEMPFAIL)
+               if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL)
                {
                        a->q_flags |= QQUEUEUP;
                        if (e->e_message == NULL)
@@ -401,10 +465,6 @@ recipient(a, sendq, e)
        }
 # endif
 
-       /* if it was an alias or a UDB expansion, just return now */
-       if (bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags))
-               goto testselfdestruct;
-
        /*
        **  If we have a level two config file, then pass the name through
        **  Ruleset 5 before sending it off.  Ruleset 5 has the right
@@ -418,10 +478,11 @@ recipient(a, sendq, e)
                        ConfigLevel, RewriteRules[5]);
                printaddr(a, FALSE);
        }
-       if (!bitset(QNOTREMOTE, a->q_flags) && ConfigLevel >= 2 &&
-           RewriteRules[5] != NULL)
+       if (!bitset(QNOTREMOTE|QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
+           ConfigLevel >= 2 && RewriteRules[5] != NULL &&
+           bitnset(M_TRYRULESET5, m->m_flags))
        {
-               maplocaluser(a, sendq, e);
+               maplocaluser(a, sendq, aliaslevel + 1, e);
        }
 
        /*
@@ -429,7 +490,8 @@ recipient(a, sendq, e)
        **  and deliver it.
        */
 
-       if (!bitset(QDONTSEND|QQUEUEUP, a->q_flags))
+       if (!bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
+           bitnset(M_HASPWENT, m->m_flags))
        {
                auto bool fuzzy;
                register struct passwd *pw;
@@ -440,11 +502,13 @@ recipient(a, sendq, e)
                if (pw == NULL)
                {
                        a->q_flags |= QBADADDR;
-                       giveresponse(EX_NOUSER, m, NULL, a->q_alias, e);
+                       a->q_status = "5.1.1";
+                       giveresponse(EX_NOUSER, m, NULL, a->q_alias,
+                                    (time_t) 0, e);
                }
                else
                {
-                       char nbuf[MAXNAME];
+                       char nbuf[MAXNAME + 1];
 
                        if (fuzzy)
                        {
@@ -453,9 +517,10 @@ recipient(a, sendq, e)
                                if (findusercount++ > 3)
                                {
                                        a->q_flags |= QBADADDR;
+                                       a->q_status = "5.4.6";
                                        usrerr("554 aliasing/forwarding loop for %s broken",
                                                pw->pw_name);
-                                       return (a);
+                                       goto done;
                                }
 
                                /* see if it aliases */
@@ -473,36 +538,103 @@ recipient(a, sendq, e)
                        buildfname(pw->pw_gecos, pw->pw_name, nbuf);
                        if (nbuf[0] != '\0')
                                a->q_fullname = newstr(nbuf);
-                       if (pw->pw_shell != NULL && pw->pw_shell[0] != '\0' &&
-                           !usershellok(pw->pw_shell))
+                       if (!usershellok(pw->pw_name, pw->pw_shell))
                        {
                                a->q_flags |= QBOGUSSHELL;
                        }
-                       if (!quoted)
-                               forward(a, sendq, e);
+                       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, TRUE);
+               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))
        {
-               q = *sendq;
-               while (q != NULL && bitset(QDONTSEND, q->q_flags))
-                       q = q->q_next;
+               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;
+
+               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);
+               }
+       }
+
        return (a);
 }
 \f/*
@@ -535,14 +667,14 @@ finduser(name, fuzzyp)
 {
        register struct passwd *pw;
        register char *p;
-       extern struct passwd *getpwent();
-       extern struct passwd *getpwnam();
+       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))
@@ -553,16 +685,35 @@ finduser(name, fuzzyp)
                        printf("failed (numeric input)\n");
                return NULL;
        }
+#endif
 
        /* look up this login name using fast path */
-       if ((pw = getpwnam(name)) != NULL)
+       if ((pw = sm_getpwnam(name)) != NULL)
        {
                if (tTd(29, 4))
                        printf("found (non-fuzzy)\n");
                return (pw);
        }
 
-#ifdef MATCHGECOS
+       /* 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)
        {
@@ -580,7 +731,17 @@ finduser(name, fuzzyp)
        (void) setpwent();
        while ((pw = getpwent()) != NULL)
        {
-               char buf[MAXNAME];
+               char buf[MAXNAME + 1];
+
+# if 0
+               if (strcasecmp(pw->pw_name, name) == 0)
+               {
+                       if (tTd(29, 4))
+                               printf("found (case wrapped)\n");
+                       *fuzzyp = TRUE;
+                       return pw;
+               }
+# endif
 
                buildfname(pw->pw_gecos, pw->pw_name, buf);
                if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name))
@@ -632,90 +793,53 @@ writable(filename, ctladdr, flags)
 {
        uid_t euid;
        gid_t egid;
-       int bits;
-       register char *p;
        char *uname;
-       struct stat stb;
-       extern char RealUserName[];
 
-       if (tTd(29, 5))
-               printf("writable(%s, %x)\n", filename, flags);
-
-#ifdef HASLSTAT
-       if ((bitset(SFF_NOSLINK, flags) ? lstat(filename, &stb)
-                                       : stat(filename, &stb)) < 0)
-#else
-       if (stat(filename, &stb) < 0)
-#endif
-       {
-               /* file does not exist -- see if directory is safe */
-               p = strrchr(filename, '/');
-               if (p == NULL)
-               {
-                       errno = ENOTDIR;
-                       return FALSE;
-               }
-               *p = '\0';
-               errno = safefile(filename, RealUid, RealGid, RealUserName,
-                                SFF_MUSTOWN, S_IWRITE|S_IEXEC);
-               *p = '/';
-               return errno == 0;
-       }
-
-#ifdef SUID_ROOT_FILES_OK
-       /* really ought to be passed down -- and not a good idea */
-       flags |= SFF_ROOTOK;
-#endif
+       if (tTd(44, 5))
+               printf("writable(%s, 0x%x)\n", filename, flags);
 
        /*
        **  File does exist -- check that it is writable.
        */
 
-       if (bitset(0111, stb.st_mode))
-       {
-               if (tTd(29, 5))
-                       printf("failed (mode %o: x bits)\n", stb.st_mode);
-               errno = EPERM;
-               return (FALSE);
-       }
-
        if (ctladdr != NULL && geteuid() == 0)
        {
                euid = ctladdr->q_uid;
                egid = ctladdr->q_gid;
                uname = ctladdr->q_user;
        }
-       else
+       else if (bitset(SFF_RUNASREALUID, flags))
        {
                euid = RealUid;
                egid = RealGid;
                uname = RealUserName;
        }
-       if (euid == 0)
+       else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags))
        {
-               euid = DefUid;
-               uname = DefUser;
+               euid = FileMailer->m_uid;
+               egid = FileMailer->m_gid;
+               uname = NULL;
+       }
+       else
+       {
+               euid = egid = 0;
+               uname = NULL;
        }
-       if (egid == 0)
-               egid = DefGid;
-       if (geteuid() == 0)
+       if (!bitset(SFF_ROOTOK, flags))
        {
-               if (bitset(S_ISUID, stb.st_mode) &&
-                   (stb.st_uid != 0 || bitset(SFF_ROOTOK, flags)))
+               if (euid == 0)
                {
-                       euid = stb.st_uid;
-                       uname = NULL;
+                       euid = DefUid;
+                       uname = DefUser;
                }
-               if (bitset(S_ISGID, stb.st_mode) &&
-                   (stb.st_gid != 0 || bitset(SFF_ROOTOK, flags)))
-                       egid = stb.st_gid;
+               if (egid == 0)
+                       egid = DefGid;
        }
+       if (geteuid() == 0 &&
+           (ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags)))
+               flags |= SFF_SETUIDOK;
 
-       if (tTd(29, 5))
-               printf("\teu/gid=%d/%d, st_u/gid=%d/%d\n",
-                       euid, egid, stb.st_uid, stb.st_gid);
-
-       errno = safefile(filename, euid, egid, uname, flags, S_IWRITE);
+       errno = safefile(filename, euid, egid, uname, flags, S_IWRITE, NULL);
        return errno == 0;
 }
 \f/*
@@ -730,6 +854,8 @@ writable(filename, ctladdr, flags)
 **                     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
@@ -753,32 +879,29 @@ writable(filename, ctladdr, flags)
 */
 
 static jmp_buf CtxIncludeTimeout;
-static int     includetimeout();
-
-#ifndef S_IWOTH
-# define S_IWOTH       (S_IWRITE >> 6)
-#endif
+static void    includetimeout();
 
 int
-include(fname, forwarding, ctladdr, sendq, e)
+include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
        char *fname;
        bool forwarding;
        ADDRESS *ctladdr;
        ADDRESS **sendq;
+       int aliaslevel;
        ENVELOPE *e;
 {
-       register FILE *fp = NULL;
+       FILE *volatile fp = NULL;
        char *oldto = e->e_to;
        char *oldfilename = FileName;
        int oldlinenumber = LineNumber;
        register EVENT *ev = NULL;
        int nincludes;
        register ADDRESS *ca;
-       uid_t saveduid, uid;
-       gid_t savedgid, gid;
-       char *uname;
+       volatile uid_t saveduid, uid;
+       volatile gid_t savedgid, gid;
+       char *volatile uname;
        int rval = 0;
-       int sfflags = forwarding ? SFF_MUSTOWN : SFF_ANYFILE;
+       int sfflags = SFF_REGONLY;
        struct stat st;
        char buf[MAXLINE];
 #ifdef _POSIX_CHOWN_RESTRICTED
@@ -813,30 +936,47 @@ include(fname, forwarding, ctladdr, sendq, e)
        if (tTd(27, 9))
                printf("include: old uid = %d/%d\n", getuid(), geteuid());
 
+       if (forwarding)
+               sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOSLINK;
+
        ca = getctladdr(ctladdr);
        if (ca == NULL)
        {
                uid = DefUid;
                gid = DefGid;
                uname = DefUser;
-               saveduid = -1;
        }
        else
        {
                uid = ca->q_uid;
                gid = ca->q_gid;
                uname = ca->q_user;
-#ifdef HASSETREUID
-               saveduid = geteuid();
-               savedgid = getegid();
-               if (saveduid == 0)
-               {
+       }
+#if HASSETREUID || USESETEUID
+       saveduid = geteuid();
+       savedgid = getegid();
+       if (saveduid == 0)
+       {
+               if (!DontInitGroups)
                        initgroups(uname, gid);
-                       if (uid != 0)
-                               (void) setreuid(0, uid);
+               if (gid != 0)
+                       (void) setgid(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
+                       else
+                               sfflags |= SFF_NOPATHCHECK;
                }
-#endif                   
        }
+#endif
 
        if (tTd(27, 9))
                printf("include: new uid = %d/%d\n", getuid(), geteuid());
@@ -855,10 +995,13 @@ include(fname, forwarding, ctladdr, sendq, e)
                rval = EOPENTIMEOUT;
                goto resetuid;
        }
-       ev = setevent((time_t) 60, includetimeout, 0);
+       if (TimeOuts.to_fileopen > 0)
+               ev = setevent(TimeOuts.to_fileopen, includetimeout, 0);
+       else
+               ev = NULL;
 
        /* the input file must be marked safe */
-       rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD);
+       rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD, NULL);
        if (rval != 0)
        {
                /* don't use this :include: file */
@@ -876,17 +1019,29 @@ include(fname, forwarding, ctladdr, sendq, e)
                                printf("include: open: %s\n", errstring(rval));
                }
        }
-       clrevent(ev);
+       if (ev != NULL)
+               clrevent(ev);
 
 resetuid:
 
-#ifdef HASSETREUID
+#if HASSETREUID || USESETEUID
        if (saveduid == 0)
        {
                if (uid != 0)
-                       if (setreuid(-1, 0) < 0 || setreuid(RealUid, 0) < 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
@@ -924,25 +1079,34 @@ resetuid:
        }
        else
        {
-               char *sh;
                register struct passwd *pw;
 
-               sh = "/SENDMAIL/ANY/SHELL/";
-               pw = getpwuid(st.st_uid);
-               if (pw != NULL)
-               {
-                       ctladdr->q_ruser = newstr(pw->pw_name);
-                       if (safechown)
-                               sh = pw->pw_shell;
-               }
+               pw = sm_getpwuid(st.st_uid);
                if (pw == NULL)
                        ctladdr->q_flags |= QBOGUSSHELL;
-               else if(!usershellok(sh))
+               else
                {
+                       char *sh;
+
+                       ctladdr->q_ruser = newstr(pw->pw_name);
                        if (safechown)
-                               ctladdr->q_flags |= QBOGUSSHELL;
+                               sh = pw->pw_shell;
                        else
-                               ctladdr->q_flags |= QUNSAFEADDR;
+                               sh = "/SENDMAIL/ANY/SHELL/";
+                       if (!usershellok(pw->pw_name, sh))
+                       {
+#ifdef LOG
+                               if (LogLevel >= 12)
+                                       syslog(LOG_INFO, "%s: user %s has bad shell %s, marked %s",
+                                               shortenstring(fname, 203),
+                                               pw->pw_name, sh,
+                                               safechown ? "bogus" : "unsafe");
+#endif
+                               if (safechown)
+                                       ctladdr->q_flags |= QBOGUSSHELL;
+                               else
+                                       ctladdr->q_flags |= QUNSAFEADDR;
+                       }
                }
        }
 
@@ -966,7 +1130,15 @@ resetuid:
        */
 
        if (bitset(S_IWOTH, st.st_mode))
+       {
+#ifdef LOG
+               if (LogLevel >= 12)
+                       syslog(LOG_INFO, "%s: world writable %s file, marked unsafe",
+                               shortenstring(fname, 203),
+                               forwarding ? "forward" : ":include:");
+#endif
                ctladdr->q_flags |= QUNSAFEADDR;
+       }
 
        /* read the file -- each line is a comma-separated list. */
        FileName = fname;
@@ -982,19 +1154,33 @@ resetuid:
                        *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);
 #ifdef LOG
                if (forwarding && LogLevel > 9)
-                       syslog(LOG_INFO, "%s: forward %s => %s",
+                       syslog(LOG_INFO, "%s: forward %.200s => %s",
                                e->e_id == NULL ? "NOQUEUE" : e->e_id,
-                               oldto, buf);
+                               oldto, shortenstring(buf, 203));
 #endif
 
-               AliasLevel++;
-               nincludes += sendtolist(buf, ctladdr, sendq, e);
-               AliasLevel--;
+               nincludes += sendtolist(buf, ctladdr, sendq, aliaslevel + 1, e);
        }
 
        if (ferror(fp) && tTd(27, 3))
@@ -1016,7 +1202,7 @@ resetuid:
        return rval;
 }
 
-static
+static void
 includetimeout()
 {
        longjmp(CtxIncludeTimeout, 1);
@@ -1036,6 +1222,7 @@ includetimeout()
 **                     send queue.
 */
 
+void
 sendtoargv(argv, e)
        register char **argv;
        register ENVELOPE *e;
@@ -1044,7 +1231,7 @@ sendtoargv(argv, e)
 
        while ((p = *argv++) != NULL)
        {
-               (void) sendtolist(p, NULLADDR, &e->e_sendqueue, e);
+               (void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e);
        }
 }
 \f/*
@@ -1070,3 +1257,93 @@ getctladdr(a)
                a = a->q_alias;
        return (a);
 }
+\f/*
+**  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;
+}
index 214dca5..78c9149 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)savemail.c 8.29 (Berkeley) 5/10/94";
+static char sccsid[] = "@(#)savemail.c 8.87 (Berkeley) 10/28/95";
 #endif /* not lint */
 
 # include "sendmail.h"
-# include <pwd.h>
 
 /*
 **  SAVEMAIL -- Save mail on error
@@ -49,6 +48,8 @@ static char sccsid[] = "@(#)savemail.c        8.29 (Berkeley) 5/10/94";
 **
 **     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
@@ -74,8 +75,10 @@ static char sccsid[] = "@(#)savemail.c       8.29 (Berkeley) 5/10/94";
 # endif
 
 
-savemail(e)
+void
+savemail(e, sendbody)
        register ENVELOPE *e;
+       bool sendbody;
 {
        register struct passwd *pw;
        register FILE *fp;
@@ -83,8 +86,8 @@ savemail(e)
        auto ADDRESS *q = NULL;
        register char *p;
        MCI mcibuf;
+       int sfflags;
        char buf[MAXLINE+1];
-       extern struct passwd *getpwnam();
        extern char *ttypath();
        typedef int (*fnptr)();
        extern bool writable();
@@ -143,11 +146,6 @@ savemail(e)
                break;
 
          case EM_BERKNET:
-               /* mail back, but return o.k. exit status */
-               ExitStat = EX_OK;
-
-               /* fall through.... */
-
          case EM_MAIL:
                state = ESM_MAIL;
                break;
@@ -187,7 +185,7 @@ savemail(e)
                switch (state)
                {
                  case ESM_QUIET:
-                       if (e->e_from.q_mailer == LocalMailer)
+                       if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
                                state = ESM_DEADLETTER;
                        else
                                state = ESM_MAIL;
@@ -207,7 +205,7 @@ savemail(e)
                                break;
                        }
 
-                       expand("\201n", buf, &buf[sizeof buf - 1], e);
+                       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)
@@ -256,12 +254,12 @@ savemail(e)
                        if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
                        {
                                (void) sendtolist("postmaster",
-                                         NULLADDR, &e->e_errorqueue, e);
+                                         NULLADDR, &e->e_errorqueue, 0, e);
                        }
-                       if (strcmp(e->e_from.q_paddr, "<>") != 0)
+                       if (!emptyaddr(&e->e_from))
                        {
                                (void) sendtolist(e->e_from.q_paddr,
-                                         NULLADDR, &e->e_errorqueue, e);
+                                         NULLADDR, &e->e_errorqueue, 0, e);
                        }
 
                        /*
@@ -285,7 +283,7 @@ savemail(e)
                                break;
                        }
                        if (returntosender(e->e_message, e->e_errorqueue,
-                                          (e->e_class >= 0), e) == 0)
+                                          sendbody, e) == 0)
                        {
                                state = ESM_DONE;
                                break;
@@ -301,15 +299,14 @@ savemail(e)
                        */
 
                        q = NULL;
-                       if (sendtolist("postmaster", NULL, &q, e) <= 0)
+                       if (sendtolist("postmaster", NULL, &q, 0, e) <= 0)
                        {
                                syserr("553 cannot parse postmaster!");
                                ExitStat = EX_SOFTWARE;
                                state = ESM_USRTMP;
                                break;
                        }
-                       if (returntosender(e->e_message,
-                                          q, (e->e_class >= 0), e) == 0)
+                       if (returntosender(e->e_message, q, sendbody, e) == 0)
                        {
                                state = ESM_DONE;
                                break;
@@ -330,44 +327,36 @@ savemail(e)
                        */
 
                        p = NULL;
-                       if (e->e_from.q_mailer == LocalMailer)
+                       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 = getpwnam(e->e_from.q_user)) != NULL)
+                               else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL)
                                        p = pw->pw_dir;
                        }
-                       if (p == NULL)
+                       if (p == NULL || e->e_dfp == NULL)
                        {
-                               /* no local directory */
+                               /* no local directory or no data file */
                                state = ESM_MAIL;
                                break;
                        }
-                       if (e->e_dfp != NULL)
+
+                       /* we have a home directory; write dead.letter */
+                       define('z', p, e);
+                       expand("\201z/dead.letter", buf, sizeof buf, e);
+                       sfflags = SFF_NOSLINK|SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
+                       e->e_to = buf;
+                       if (mailfile(buf, NULL, sfflags, e) == EX_OK)
                        {
                                bool oldverb = Verbose;
 
-                               /* we have a home directory; open dead.letter */
-                               define('z', p, e);
-                               expand("\201z/dead.letter", buf, &buf[sizeof buf - 1], e);
                                Verbose = TRUE;
-                               message("Saving message in %s", buf);
+                               message("Saved message in %s", buf);
                                Verbose = oldverb;
-                               e->e_to = buf;
-                               q = NULL;
-                               (void) sendtolist(buf, &e->e_from, &q, e);
-                               if (q != NULL &&
-                                   !bitset(QBADADDR, q->q_flags) &&
-                                   deliver(e, q) == 0)
-                                       state = ESM_DONE;
-                               else
-                                       state = ESM_MAIL;
-                       }
-                       else
-                       {
-                               /* no data file -- try mailing back */
-                               state = ESM_MAIL;
+                               state = ESM_DONE;
+                               break;
                        }
+                       state = ESM_MAIL;
                        break;
 
                  case ESM_USRTMP:
@@ -381,15 +370,19 @@ savemail(e)
                                break;
                        }
 
-                       strcpy(buf, _PATH_VARTMP);
-                       strcat(buf, "dead.letter");
-                       if (!writable(buf, NULLADDR, SFF_NOSLINK))
+                       if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')
                        {
                                state = ESM_PANIC;
                                break;
                        }
-                       fp = dfopen(buf, O_WRONLY|O_CREAT|O_APPEND, FileMode);
-                       if (fp == NULL)
+
+                       strcpy(buf, _PATH_VARTMP);
+                       strcat(buf, "dead.letter");
+
+                       sfflags = SFF_NOSLINK|SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT;
+                       if (!writable(buf, NULL, sfflags) ||
+                           (fp = safefopen(buf, O_WRONLY|O_CREAT|O_APPEND,
+                                           FileMode, sfflags)) == NULL)
                        {
                                state = ESM_PANIC;
                                break;
@@ -402,13 +395,26 @@ savemail(e)
                                mcibuf.mci_flags |= MCIF_7BIT;
 
                        putfromline(&mcibuf, e);
-                       (*e->e_puthdr)(&mcibuf, e);
-                       putline("\n", &mcibuf);
+                       (*e->e_puthdr)(&mcibuf, e->e_header, e);
                        (*e->e_putbody)(&mcibuf, e, NULL);
                        putline("\n", &mcibuf);
                        (void) fflush(fp);
-                       state = ferror(fp) ? ESM_PANIC : ESM_DONE;
-                       (void) xfclose(fp, "savemail", "/usr/tmp/dead.letter");
+                       if (!ferror(fp))
+                       {
+                               bool oldverb = Verbose;
+
+                               Verbose = TRUE;
+                               message("Saved message in %s", buf);
+                               Verbose = oldverb;
+#ifdef LOG
+                               if (LogLevel > 3)
+                                       syslog(LOG_NOTICE, "Saved message in %s", buf);
+#endif
+                               state = ESM_DONE;
+                               break;
+                       }
+                       state = ESM_PANIC;
+                       (void) xfclose(fp, "savemail", buf);
                        break;
 
                  default:
@@ -418,6 +424,7 @@ savemail(e)
 
                  case ESM_PANIC:
                        /* leave the locked queue & transcript files around */
+                       loseqfile(e, "savemail panic");
                        syserr("!554 savemail: cannot save rejected email anywhere");
                }
        }
@@ -441,24 +448,24 @@ savemail(e)
 **             mail.
 */
 
-static bool    SendBody;
-
 #define MAXRETURNS     6       /* max depth of returning messages */
 #define ERRORFUDGE     100     /* nominal size of error message text */
 
+int
 returntosender(msg, returnq, sendbody, e)
        char *msg;
        ADDRESS *returnq;
        bool sendbody;
        register ENVELOPE *e;
 {
-       char buf[MAXNAME];
-       extern putheader(), errbody();
        register ENVELOPE *ee;
        ENVELOPE *oldcur = CurEnv;
        ENVELOPE errenvelope;
        static int returndepth;
        register ADDRESS *q;
+       char *p;
+       char buf[MAXNAME + 1];
+       extern void errbody __P((MCI *, ENVELOPE *, char *));
 
        if (returnq == NULL)
                return (-1);
@@ -468,9 +475,14 @@ returntosender(msg, returnq, sendbody, e)
 
        if (tTd(6, 1))
        {
-               printf("Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=",
+               printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=",
                       msg, returndepth, e);
                printaddr(returnq, TRUE);
+               if (tTd(6, 20))
+               {
+                       printf("Sendq=");
+                       printaddr(e->e_sendqueue, TRUE);
+               }
        }
 
        if (++returndepth >= MAXRETURNS)
@@ -482,9 +494,10 @@ returntosender(msg, returnq, sendbody, e)
                return (0);
        }
 
-       SendBody = sendbody;
        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);
@@ -497,45 +510,97 @@ returntosender(msg, returnq, sendbody, e)
                ee->e_flags &= ~EF_OLDSTYLE;
        ee->e_sendqueue = returnq;
        ee->e_msgsize = ERRORFUDGE;
-       if (!NoReturn)
+       if (sendbody)
                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;
 
-               if (!bitset(QDONTSEND, q->q_flags))
-                       ee->e_nrcpts++;
-
                if (!DontPruneRoutes && pruneroute(q->q_paddr))
+               {
+                       register ADDRESS *p;
+
                        parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e);
+                       for (p = returnq; p != NULL; p = p->q_next)
+                       {
+                               if (p != q && sameaddr(p, q))
+                                       q->q_flags |= QDONTSEND;
+                       }
+               }
+
+               if (!bitset(QDONTSEND, q->q_flags))
+                       ee->e_nrcpts++;
 
                if (q->q_alias == NULL)
-                       addheader("To", q->q_paddr, ee);
+                       addheader("To", q->q_paddr, &ee->e_header);
        }
 
 # ifdef LOG
        if (LogLevel > 5)
-               syslog(LOG_INFO, "%s: %s: return to sender: %s",
-                       e->e_id, ee->e_id, msg);
+       {
+               if (bitset(EF_RESPONSE|EF_WARNING, e->e_flags))
+                       p = "return to sender";
+               else
+                       p = "postmaster notify";
+               syslog(LOG_INFO, "%s: %s: %s: %s",
+                       e->e_id, ee->e_id, p, shortenstring(msg, 203));
+       }
 # endif
 
-       (void) sprintf(buf, "Returned mail: %.*s", sizeof buf - 20, msg);
-       addheader("Subject", buf, ee);
        if (SendMIMEErrors)
        {
-               addheader("MIME-Version", "1.0", ee);
-               (void) sprintf(buf, "%s.%ld/%s",
+               addheader("MIME-Version", "1.0", &ee->e_header);
+
+               (void) sprintf(buf, "%s.%ld/%.100s",
                        ee->e_id, curtime(), MyHostName);
                ee->e_msgboundary = newstr(buf);
-               (void) sprintf(buf, "multipart/mixed; boundary=\"%s\"",
-                                       ee->e_msgboundary);
-               addheader("Content-Type", buf, ee);
+               (void) sprintf(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
+       {
+               sprintf(buf, "Returned mail: %.*s", sizeof buf - 20, msg);
+               addheader("Subject", buf, &ee->e_header);
+               p = "failure";
        }
+       (void) sprintf(buf, "auto-generated (%s)", p);
+       addheader("Auto-Submitted", buf, &ee->e_header);
 
        /* fake up an address header for the from person */
-       expand("\201n", buf, &buf[sizeof buf - 1], e);
+       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!");
@@ -562,8 +627,15 @@ returntosender(msg, returnq, sendbody, e)
        CurEnv = oldcur;
        returndepth--;
 
-       /* should check for delivery errors here */
-       return (0);
+       /* 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(QSENT, q->q_flags))
+                       return 0;
+       }
+       return -1;
 }
 \f/*
 **  ERRBODY -- output the body of an error message.
@@ -574,6 +646,8 @@ returntosender(msg, returnq, sendbody, e)
 **     Parameters:
 **             mci -- the mailer connection information.
 **             e -- the envelope we are working in.
+**             separator -- any possible MIME separator.
+**             flags -- to modify the behaviour.
 **
 **     Returns:
 **             none
@@ -582,16 +656,25 @@ returntosender(msg, returnq, sendbody, e)
 **             Outputs the body of an error message.
 */
 
-errbody(mci, e)
+void
+errbody(mci, e, separator)
        register MCI *mci;
        register ENVELOPE *e;
+       char *separator;
 {
        register FILE *xfile;
        char *p;
        register ADDRESS *q;
        bool printheader;
+       bool sendbody;
        char buf[MAXLINE];
+       extern char *xuntextify();
 
+       if (bitset(MCIF_INHEADER, mci->mci_flags))
+       {
+               putline("", mci);
+               mci->mci_flags &= ~MCIF_INHEADER;
+       }
        if (e->e_parent == NULL)
        {
                syserr("errbody: null parent");
@@ -635,7 +718,7 @@ errbody(mci, e)
        sprintf(buf, "The original message was received at %s",
                arpadate(ctime(&e->e_parent->e_ctime)));
        putline(buf, mci);
-       expand("from \201_", buf, &buf[sizeof buf - 1], e->e_parent);
+       expand("from \201_", buf, sizeof buf, e->e_parent);
        putline(buf, mci);
        putline("", mci);
 
@@ -643,7 +726,7 @@ errbody(mci, e)
        **  Output error message header (if specified and available).
        */
 
-       if (ErrMsgFile != NULL)
+       if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
        {
                if (*ErrMsgFile == '/')
                {
@@ -652,7 +735,7 @@ errbody(mci, e)
                        {
                                while (fgets(buf, sizeof buf, xfile) != NULL)
                                {
-                                       expand(buf, buf, &buf[sizeof buf - 1], e);
+                                       expand(buf, buf, sizeof buf, e);
                                        putline(buf, mci);
                                }
                                (void) fclose(xfile);
@@ -661,7 +744,7 @@ errbody(mci, e)
                }
                else
                {
-                       expand(ErrMsgFile, buf, &buf[sizeof buf - 1], e);
+                       expand(ErrMsgFile, buf, sizeof buf, e);
                        putline(buf, mci);
                        putline("", mci);
                }
@@ -674,27 +757,46 @@ errbody(mci, e)
        printheader = TRUE;
        for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
        {
-               if (bitset(QBADADDR|QREPORT, q->q_flags))
+               if (bitset(QBADADDR, q->q_flags))
                {
-                       if (printheader)
-                       {
-                               putline("   ----- The following addresses had delivery problems -----",
-                                       mci);
-                               printheader = FALSE;
-                       }
-                       strcpy(buf, q->q_paddr);
-                       if (bitset(QBADADDR, q->q_flags))
-                               strcat(buf, "  (unrecoverable error)");
+                       if (!bitset(QPINGONFAILURE, q->q_flags))
+                               continue;
+                       p = "unrecoverable error";
+               }
+               else if (!bitset(QPRIMARY, q->q_flags))
+                       continue;
+               else if (bitset(QDELAYED, q->q_flags))
+                       p = "transient failure";
+               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
-                               strcat(buf, "  (transient failure)");
+                               p = "successfully delivered to mailbox";
+               }
+               else if (bitset(QEXPANDED, q->q_flags))
+                       p = "expanded by alias";
+               else
+                       continue;
+
+               if (printheader)
+               {
+                       putline("   ----- The following addresses have delivery notifications -----",
+                               mci);
+                       printheader = FALSE;
+               }
+
+               sprintf(buf, "%s  (%s)", shortenstring(q->q_paddr, 203), p);
+               putline(buf, mci);
+               if (q->q_alias != NULL)
+               {
+                       sprintf(buf, "    (expanded from: %s)",
+                               shortenstring(q->q_alias->q_paddr, 203));
                        putline(buf, mci);
-                       if (q->q_alias != NULL)
-                       {
-                               strcpy(buf, "    (expanded from: ");
-                               strcat(buf, q->q_alias->q_paddr);
-                               strcat(buf, ")");
-                               putline(buf, mci);
-                       }
                }
        }
        if (!printheader)
@@ -713,46 +815,256 @@ errbody(mci, e)
        }
        else
        {
-               putline("   ----- Transcript of session follows -----\n", mci);
+               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) sprintf(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) sprintf(buf, "Original-Envelope-Id: %.800s",
+                               xuntextify(e->e_parent->e_envid));
+                       putline(buf, mci);
+               }
+
+               /* Reporting-MTA: is us (required) */
+               (void) sprintf(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) sprintf(buf, "Received-From-MTA: %s; %.800s",
+                               p, RealHostName);
+                       putline(buf, mci);
+               }
+
+               /* Arrival-Date: -- when it arrived here */
+               (void) sprintf(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) sprintf(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) sprintf(buf, "Final-Recipient: %s; %.700s@%.100s",
+                                       p, r->q_user, MyHostName);
+                       }
+                       else
+                       {
+                               (void) sprintf(buf, "Final-Recipient: %s; %.800s",
+                                       p, r->q_user);
+                       }
+                       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) sprintf(buf, "X-Actual-Recipient: %s; %.700s@%.100s",
+                                               p, q->q_user, MyHostName);
+                               }
+                               else
+                               {
+                                       (void) sprintf(buf, "X-Actual-Recipient: %s; %.800s",
+                                               p, q->q_user);
+                               }
+                               putline(buf, mci);
+                       }
+
+                       /* Action: -- what happened? */
+                       sprintf(buf, "Action: %s", action);
+                       putline(buf, mci);
+
+                       /* Status: -- what _really_ happened? */
+                       strcpy(buf, "Status: ");
+                       if (q->q_status != NULL)
+                               strcat(buf, q->q_status);
+                       else if (bitset(QBADADDR, q->q_flags))
+                               strcat(buf, "5.0.0");
+                       else if (bitset(QQUEUEUP, q->q_flags))
+                               strcat(buf, "4.0.0");
+                       else
+                               strcat(buf, "2.0.0");
+                       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) sprintf(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) sprintf(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) sprintf(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];
+                               sprintf(buf, "Will-Retry-Until: %s",
+                                       arpadate(ctime(&xdate)));
+                               putline(buf, mci);
+                       }
+               }
+       }
+#endif
+
        /*
        **  Output text of original message
        */
 
-       if (NoReturn)
-               SendBody = FALSE;
        putline("", mci);
-       if (e->e_parent->e_df != NULL)
+       if (bitset(EF_HAS_DF, e->e_parent->e_flags))
        {
-               if (SendBody)
-                       putline("   ----- Original message follows -----\n", mci);
-               else
-                       putline("   ----- Message header follows -----\n", mci);
-               (void) fflush(mci->mci_out);
+               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 (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
                {
-                       putline("", mci);
                        (void) sprintf(buf, "--%s", e->e_msgboundary);
+
                        putline(buf, mci);
-                       putline("Content-Type: message/rfc822", mci);
-                       putline("", mci);
+                       (void) sprintf(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) sprintf(buf, "Content-Transfer-Encoding: %s",
+                                       p);
+                               putline(buf, mci);
+                       }
                }
-               putheader(mci, e->e_parent);
                putline("", mci);
-               if (SendBody)
+               putheader(mci, e->e_parent->e_header, e->e_parent);
+               if (sendbody)
                        putbody(mci, e->e_parent, e->e_msgboundary);
-               else
+               else if (e->e_msgboundary == NULL)
+               {
+                       putline("", mci);
                        putline("   ----- Message body suppressed -----", mci);
+               }
        }
-       else
+       else if (e->e_msgboundary == NULL)
        {
                putline("  ----- No message was collected -----\n", mci);
        }
@@ -773,6 +1085,248 @@ errbody(mci, e)
                syserr("errbody: I/O error");
 }
 \f/*
+**  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.3";
+
+         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";
+}
+\f/*
+**  XTEXTIFY -- take regular text and turn it into DSN-style xtext
+**
+**     Parameters:
+**             t -- the text to convert.
+**
+**     Returns:
+**             The xtext-ified version of the same string.
+*/
+
+char *
+xtextify(t)
+       register char *t;
+{
+       register char *p;
+       int l;
+       int nbogus;
+       static char *bp = NULL;
+       static int bplen = 0;
+
+       /* 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 == '(')
+                       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 == '(')
+               {
+                       *p++ = '+';
+                       *p++ = "0123456789abcdef"[c >> 4];
+                       *p++ = "0123456789abcdef"[c & 0xf];
+               }
+               else
+                       *p++ = c;
+       }
+       *p = '\0';
+       return bp;
+}
+\f/*
+**  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;
+       }
+       return bp;
+}
+\f/*
+**  XTEXTOK -- check if a string is legal xtext
+**
+**     Xtext is used in Delivery Status Notifications.  The spec was
+**     taken from draft-ietf-notary-mime-delivery-04.txt.
+**
+**     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;
+}
+\f/*
 **  PRUNEROUTE -- prune an RFC-822 source route
 ** 
 **     Trims down a source route to the last internet-registered hop.
@@ -789,6 +1343,7 @@ errbody(mci, e)
 **             modifies addr in-place
 */
 
+bool
 pruneroute(addr)
        char *addr;
 {
index f03d3de..5b48ffc 100644 (file)
@@ -29,9 +29,9 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\"     @(#)sendmail.8 8.4 (Berkeley) 12/11/93
+.\"     @(#)sendmail.8 8.6 (Berkeley) 5/27/95
 .\"
-.Dd December 11, 1993
+.Dd May 27, 1995
 .Dt SENDMAIL 8
 .Os BSD 4
 .Sh NAME
@@ -209,6 +209,18 @@ is specified,
 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 Ns Ar name
 An alternate and obsolete form of the
 .Fl f
@@ -259,6 +271,7 @@ successful deliveries (default 10).
 This avoids excessive duplicate deliveries
 when sending to long mailing lists
 interrupted by system crashes.
+.ne 1i
 .It Li d Ns Ar x
 Set the delivery mode to
 .Ar x .
index 4fc1ef7..9de20ca 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -31,7 +31,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     @(#)sendmail.h  8.43.1.3 (Berkeley) 3/5/95
+ *     @(#)sendmail.h  8.159 (Berkeley) 11/18/95
  */
 
 /*
@@ -41,7 +41,7 @@
 # ifdef _DEFINE
 # define EXTERN
 # ifndef lint
-static char SmailSccsId[] =    "@(#)sendmail.h 8.43.1.3                3/5/95";
+static char SmailSccsId[] =    "@(#)sendmail.h 8.159           11/18/95";
 # endif
 # else /*  _DEFINE */
 # define EXTERN extern
@@ -53,10 +53,13 @@ static char SmailSccsId[] = "@(#)sendmail.h 8.43.1.3                3/5/95";
 # include <stdio.h>
 # include <ctype.h>
 # include <setjmp.h>
-# include <sysexits.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"
@@ -68,38 +71,42 @@ static char SmailSccsId[] = "@(#)sendmail.h 8.43.1.3                3/5/95";
 # ifdef DAEMON
 # include <sys/socket.h>
 # endif
-# ifdef NETUNIX
+# if NETUNIX
 # include <sys/un.h>
 # endif
-# ifdef NETINET
+# if NETINET
 # include <netinet/in.h>
 # endif
-# ifdef NETISO
+# if NETISO
 # include <netiso/iso.h>
 # endif
-# ifdef NETNS
+# if NETNS
 # include <netns/ns.h>
 # endif
-# ifdef NETX25
+# if NETX25
 # include <netccitt/x25.h>
 # 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 128 possible bits, or 12 8-bit bytes.
+**     This is 256 possible bits, or 32 8-bit bytes.
 */
 
-#define BITMAPBYTES    16      /* number of bytes in a bit map */
+#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))))
+#define _BITWORD(bit)  ((bit) / (BYTEBITS * sizeof (int)))
+#define _BITBIT(bit)   (1 << ((bit) % (BYTEBITS * sizeof (int))))
 
 typedef int    BITMAP[BITMAPBYTES / sizeof (int)];
 
@@ -126,7 +133,7 @@ struct address
        char            *q_ruser;       /* real user name, or NULL if q_user */
        char            *q_host;        /* host name */
        struct mailer   *q_mailer;      /* mailer to use */
-       u_short         q_flags;        /* status flags, see below */
+       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) */
@@ -135,25 +142,51 @@ struct address
        struct address  *q_alias;       /* address this results from */
        char            *q_owner;       /* owner of q_alias */
        struct address  *q_tchain;      /* temporary use chain */
-       time_t          q_timeout;      /* timeout for this address */
+       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     000001  /* don't send to this address */
-# define QBADADDR      000002  /* this address is verified bad */
-# define QGOODUID      000004  /* the q_uid q_gid fields are good */
-# define QPRIMARY      000010  /* set from argv */
-# define QQUEUEUP      000020  /* queue for later transmission */
-# define QSENT         000040  /* has been successfully delivered */
-# define QNOTREMOTE    000100  /* not an address for remote forwarding */
-# define QSELFREF      000200  /* this address references itself */
-# define QVERIFIED     000400  /* verified, but not expanded */
-# define QREPORT       001000  /* report this address in return message */
-# define QBOGUSSHELL   002000  /* this entry has an invalid shell listed */
-# define QUNSAFEADDR   004000  /* address aquired through an unsafe path */
+# 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     0x80000000      /* temp: address set this pass */
 
 # define NULLADDR      ((ADDRESS *) NULL)
+
+/* functions */
+extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **, 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 *));
 \f/*
 **  Mailer definition structure.
 **     Every mailer known to the system is declared in this
@@ -170,8 +203,12 @@ 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 */
@@ -181,15 +218,18 @@ struct mailer
        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 */
 };
 
-typedef struct mailer  MAILER;
-
 /* 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 */
@@ -198,25 +238,39 @@ typedef struct mailer     MAILER;
 # 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_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_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_RESTR       'S'     /* must be daemon to execute */
+# 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_EBCDIC      '3'     /* extend Q-P encoding for EBCDIC */
+# define M_TRYRULESET5 '5'     /* use ruleset 5 after local aliasing */
 # 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 */
 
 EXTERN MAILER  *Mailer[MAXMAILERS+1];
 
@@ -255,18 +309,21 @@ struct hdrinfo
 extern struct hdrinfo  HdrInfo[];
 
 /* bits for h_flags and hi_flags */
-# define H_EOH         00001   /* this field terminates header */
-# define H_RCPT                00002   /* contains recipient addresses */
-# define H_DEFAULT     00004   /* if another value is found, drop this */
-# define H_RESENT      00010   /* this address is a "Resent-..." address */
-# define H_CHECK       00020   /* check h_mflags against m_flags */
-# define H_ACHECK      00040   /* ditto, but always (not just default) */
-# define H_FORCE       00100   /* force this field, even if default */
-# define H_TRACE       00200   /* this field contains trace information */
-# define H_FROM                00400   /* this is a from-type field */
-# define H_VALID       01000   /* this field has a validated value */
-# define H_RECEIPTTO   02000   /* this field has return receipt info */
-# define H_ERRORSTO    04000   /* this field has error address info */
+# 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 */
 \f/*
 **  Information about currently open connections to mailers, or to
 **  hosts that we have looked up recently.
@@ -288,20 +345,27 @@ MCI
        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 */
        time_t          mci_lastuse;    /* last usage time */
 };
 
 
 /* flag bits */
-#define MCIF_VALID     000001          /* this entry is valid */
-#define MCIF_TEMP      000002          /* don't cache this connection */
-#define MCIF_CACHED    000004          /* currently in open cache */
-#define MCIF_ESMTP     000010          /* this host speaks ESMTP */
-#define MCIF_EXPN      000020          /* EXPN command supported */
-#define MCIF_SIZE      000040          /* SIZE option supported */
-#define MCIF_8BITMIME  000100          /* BODY=8BITMIME supported */
-#define MCIF_7BIT      000200          /* strip this message to 7 bits */
-#define MCIF_MULTSTAT  000400          /* MAIL11V3: handles MULT status */
+#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 */
@@ -311,6 +375,11 @@ MCI
 #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 *));
 \f/*
 **  Envelope structure.
 **     This structure defines the message itself.  There is usually
@@ -320,9 +389,7 @@ MCI
 **     will have their own envelope.
 */
 
-# define ENVELOPE      struct envelope
-
-ENVELOPE
+struct envelope
 {
        HDR             *e_header;      /* head of header list */
        long            e_msgpriority;  /* adjusted priority of this message */
@@ -342,14 +409,14 @@ ENVELOPE
        short           e_nsent;        /* number of sends since checkpoint */
        short           e_sendmode;     /* message send mode */
        short           e_errormode;    /* error return mode */
-       int             (*e_puthdr)__P((MCI *, ENVELOPE *));
+       short           e_timeoutclass; /* message timeout class */
+       void            (*e_puthdr)__P((MCI *, HDR *, ENVELOPE *));
                                        /* function to put header of message */
-       int             (*e_putbody)__P((MCI *, ENVELOPE *, char *));
+       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 */
-       char            *e_df;          /* location of temp file */
        FILE            *e_dfp;         /* temporary file */
        char            *e_id;          /* code for this entry in queue */
        FILE            *e_xfp;         /* transcript file */
@@ -358,16 +425,23 @@ ENVELOPE
        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_macro[128];  /* macro definitions */
+       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_KEEPQUEUE   0x0000040       /* keep queue files always */
+#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) */
@@ -378,8 +452,23 @@ ENVELOPE
 #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 */
 
 EXTERN ENVELOPE        *CurEnv;        /* envelope currently being processed */
+
+/* functions */
+extern ENVELOPE        *newenvelope __P((ENVELOPE *, ENVELOPE *));
+extern void    dropenvelope __P((ENVELOPE *));
+extern void    clearenvelope __P((ENVELOPE *, bool));
+
+extern void    putheader __P((MCI *, HDR *, ENVELOPE *));
+extern void    putbody __P((MCI *, ENVELOPE *, char *));
 \f/*
 **  Message priority classes.
 **
@@ -434,35 +523,35 @@ EXTERN struct rewrite     *RewriteRules[MAXRWSETS];
 */
 
 /* left hand side items */
-# define MATCHZANY     0220    /* match zero or more tokens */
-# define MATCHANY      0221    /* match one or more tokens */
-# define MATCHONE      0222    /* match exactly one token */
-# define MATCHCLASS    0223    /* match one token in a class */
-# define MATCHNCLASS   0224    /* match anything not in class */
-# define MATCHREPL     0225    /* replacement on RHS for above */
+# 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      0226    /* canonical net, next token */
-# define CANONHOST     0227    /* canonical host, next token */
-# define CANONUSER     0230    /* canonical user, next N tokens */
-# define CALLSUBR      0231    /* call another rewriting set */
+# 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                0232    /* conditional if-then */
-# define CONDELSE      0233    /* conditional else */
-# define CONDFI                0234    /* conditional fi */
+# 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     0235    /* hostname lookup begin */
-# define HOSTEND       0236    /* hostname lookup end */
+# define HOSTBEGIN     ((u_char)0235)  /* hostname lookup begin */
+# define HOSTEND       ((u_char)0236)  /* hostname lookup end */
 
 /* bracket characters for generalized lookup */
-# define LOOKUPBEGIN   0205    /* generalized lookup begin */
-# define LOOKUPEND     0206    /* generalized lookup end */
+# define LOOKUPBEGIN   ((u_char)0205)  /* generalized lookup begin */
+# define LOOKUPEND     ((u_char)0206)  /* generalized lookup end */
 
 /* macro substitution character */
-# define MACROEXPAND   0201    /* macro expansion */
-# define MACRODEXPAND  0202    /* deferred macro expansion */
+# define MACROEXPAND   ((u_char)0201)  /* macro expansion */
+# define MACRODEXPAND  ((u_char)0202)  /* deferred macro expansion */
 
 /* to make the code clearer */
 # define MATCHZERO     CANONHOST
@@ -473,6 +562,16 @@ 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 **));
 \f/*
 **  Name canonification short circuit.
 **
@@ -505,6 +604,7 @@ NAMECANON
 
 # define MAPCLASS      struct _mapclass
 # define MAP           struct _map
+# define MAXMAPACTIONS 3               /* size of map_actions array */
 
 
 /*
@@ -515,32 +615,47 @@ MAP
 {
        MAPCLASS        *map_class;     /* the class of this map */
        char            *map_mname;     /* name of this map */
-       int             map_mflags;     /* flags, see below */
+       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_domain;    /* the (nominal) NIS domain */
        char            *map_rebuild;   /* program to run to do auto-rebuild */
        time_t          map_mtime;      /* last database modification time */
+       short           map_specificity;        /* specificity of alaases */
+       MAP             *map_stack[MAXMAPSTACK];   /* list for stacked maps */
+       short           map_return[MAXMAPACTIONS]; /* return bitmaps for stacked maps */
 };
 
-/* bit values for map_flags */
-# define MF_VALID      0x0001          /* this entry is valid */
-# define MF_INCLNULL   0x0002          /* include null byte in key */
-# define MF_OPTIONAL   0x0004          /* don't complain if map not found */
-# define MF_NOFOLDCASE 0x0008          /* don't fold case in keys */
-# define MF_MATCHONLY  0x0010          /* don't use the map value */
-# define MF_OPEN       0x0020          /* this entry is open */
-# define MF_WRITABLE   0x0040          /* open for writing */
-# define MF_ALIAS      0x0080          /* this is an alias file */
-# define MF_TRY0NULL   0x0100          /* try with no null byte */
-# define MF_TRY1NULL   0x0200          /* try with the null byte */
-# define MF_LOCKED     0x0400          /* this map is currently locked */
-# define MF_ALIASWAIT  0x0800          /* alias map in aliaswait state */
-# define MF_IMPL_HASH  0x1000          /* implicit: underlying hash database */
-# define MF_IMPL_NDBM  0x2000          /* implicit: underlying NDBM database */
-
+/* 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 */
+
+/* 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
@@ -567,6 +682,11 @@ MAPCLASS
 #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 *, char *, int, char **));
+extern MAP     *makemapentry __P((char *));
 \f/*
 **  Symbol table definitions
 */
@@ -587,6 +707,8 @@ struct symtab
                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 */
        }       s_value;
 };
 
@@ -602,6 +724,8 @@ typedef struct symtab       STAB;
 # 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_MCI                16      /* mailer connection info (offset) */
 
 # define s_class       s_value.sv_class
@@ -613,6 +737,8 @@ typedef struct symtab       STAB;
 # 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
 
 extern STAB            *stab __P((char *, int, int));
 extern void            stabapply __P((void (*)(STAB *, int), int));
@@ -632,7 +758,7 @@ extern void         stabapply __P((void (*)(STAB *, int), int));
 struct event
 {
        time_t          ev_time;        /* time of the function call */
-       int             (*ev_func)__P((int));
+       void            (*ev_func)__P((int));
                                        /* function to call */
        int             ev_arg;         /* argument to ev_func */
        int             ev_pid;         /* pid that set this event */
@@ -642,8 +768,12 @@ struct event
 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 *));
 \f/*
-**  Operation, send, and error modes
+**  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
@@ -673,9 +803,9 @@ EXTERN char OpMode;         /* operation mode, see below */
 
 /* values for e_sendmode -- send modes */
 #define SM_DELIVER     'i'             /* interactive delivery */
-#define SM_QUICKD      'j'             /* deliver w/o queueing */
 #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 */
@@ -688,6 +818,37 @@ EXTERN char        OpMode;         /* operation mode, see below */
 #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 */
+
+
+/* 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 *e
 \f/*
 **  Additional definitions
 */
@@ -699,16 +860,16 @@ EXTERN char       OpMode;         /* operation mode, see below */
 */
 
 #define PRIV_PUBLIC            0       /* what have I got to hide? */
-#define PRIV_NEEDMAILHELO      00001   /* insist on HELO for MAIL, at least */
-#define PRIV_NEEDEXPNHELO      00002   /* insist on HELO for EXPN */
-#define PRIV_NEEDVRFYHELO      00004   /* insist on HELO for VRFY */
-#define PRIV_NOEXPN            00010   /* disallow EXPN command entirely */
-#define PRIV_NOVRFY            00020   /* disallow VRFY command entirely */
-#define PRIV_AUTHWARNINGS      00040   /* flag possible authorization probs */
-#define PRIV_NORECEIPTS                00100   /* disallow return receipts */
-#define PRIV_RESTRICTMAILQ     01000   /* restrict mailq command */
-#define PRIV_RESTRICTQRUN      02000   /* restrict queue run */
-#define PRIV_GOAWAY            00777   /* don't give no info, anyway, anyhow */
+#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_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
@@ -722,12 +883,12 @@ struct prival
 **  Flags passed to remotename, parseaddr, allocaddr, and buildaddr.
 */
 
-#define RF_SENDERADDR          0001    /* this is a sender address */
-#define RF_HEADERADDR          0002    /* this is a header address */
-#define RF_CANONICAL           0004    /* strip comment information */
-#define RF_ADDDOMAIN           0010    /* OK to do domain extension */
-#define RF_COPYPARSE           0020    /* copy parsed user & host */
-#define RF_COPYPADDR           0040    /* copy print address */
+#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
 
@@ -740,6 +901,23 @@ struct prival
 #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 */
+
+/* flags that are actually specific to safefopen */
+#define SFF_OPENASROOT         0x1000  /* open as root instead of real user */
+
+
+/*
+**  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 */
 
 
 /*
@@ -747,27 +925,56 @@ struct prival
 **  we are forced to declare a supertype here.
 */
 
+#ifdef DAEMON
 union bigsockaddr
 {
        struct sockaddr         sa;     /* general version */
-#ifdef NETUNIX
+#if NETUNIX
        struct sockaddr_un      sunix;  /* UNIX family */
 #endif
-#ifdef NETINET
+#if NETINET
        struct sockaddr_in      sin;    /* INET family */
 #endif
-#ifdef NETISO
+#if NETISO
        struct sockaddr_iso     siso;   /* ISO family */
 #endif
-#ifdef NETNS
+#if NETNS
        struct sockaddr_ns      sns;    /* XNS family */
 #endif
-#ifdef NETX25
+#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    *anynet_ntoa __P((SOCKADDR *));
+
+#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 */
 \f/*
 **  Global variables.
 */
@@ -778,7 +985,6 @@ EXTERN bool IgnrDot;        /* don't let dot end messages */
 EXTERN bool    SaveFrom;       /* save leading "From" lines */
 EXTERN bool    Verbose;        /* set if blow-by-blow desired */
 EXTERN bool    GrabTo;         /* if set, get recipients from msg */
-EXTERN bool    NoReturn;       /* don't return letter to sender */
 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 */
@@ -787,20 +993,22 @@ 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;  /* use internet domain name server */
-EXTERN bool    SevenBit;       /* force 7-bit data */
+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 time_t  SafeAlias;      /* interval to wait until @:@ in alias file */
 EXTERN FILE    *InChannel;     /* input connection */
 EXTERN FILE    *OutChannel;    /* output connection */
-EXTERN uid_t   RealUid;        /* when Daemon, real uid of caller */
-EXTERN gid_t   RealGid;        /* when Daemon, real gid of caller */
+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 int     OldUmask;       /* umask when sendmail starts up */
 EXTERN int     Errors;         /* set if errors (local to single pass) */
 EXTERN int     ExitStat;       /* exit status code */
-EXTERN int     AliasLevel;     /* depth of aliasing */
 EXTERN int     LineNumber;     /* line number in current input */
 EXTERN int     LogLevel;       /* level of logging to perform */
 EXTERN int     FileMode;       /* mode on files */
@@ -817,7 +1025,6 @@ 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 SOCKADDR RealHostAddr;  /* address 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 */
@@ -826,9 +1033,10 @@ 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    CheckLoopBack;  /* check for loopback on HELO packet */
 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] */
@@ -845,16 +1053,38 @@ 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  MaxHostStatAge; /* max age of cached host status info */
+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 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 bool    SingleLineFromHeader;   /* force From: header to be one line */
+EXTERN int     MaxAliasRecursion;      /* maximum depth of alias 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 int     DeliveryNiceness;       /* how nice to be during delivery */
 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    BrokenSmtpPeers;        /* peers can't handle 2-line greeting */
+EXTERN bool    DontExpandCnames;       /* do not $[...$] expand CNAMEs */
 EXTERN int     MaxMciCache;            /* maximum entries in MCI cache */
 EXTERN time_t  MciCacheTimeout;        /* maximum idle time on connections */
+EXTERN time_t  MciInfoTimeout;         /* how long 'til we retry down hosts */
 EXTERN char    *QueueLimitRecipient;   /* limit queue runs to this recipient */
 EXTERN char    *QueueLimitSender;      /* limit queue runs to this sender */
 EXTERN char    *QueueLimitId;          /* limit queue runs to this id */
 EXTERN FILE    *TrafficLogFile;        /* file in which to log all traffic */
+EXTERN char    *UserEnviron[MAXUSERENVIRON + 1];
+                                       /* saved user environment */
 extern int     errno;
 
 
@@ -875,16 +1105,23 @@ EXTERN struct
        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_connect;     /* initial connection timeout */
        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;    /* queue return timeout */
-       time_t  to_q_warning;   /* queue warning timeout */
+       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
@@ -920,43 +1157,59 @@ EXTERN u_char    tTdvect[100];
 **  Declarations of useful functions
 */
 
-extern ADDRESS         *parseaddr __P((char *, ADDRESS *, int, int, char **, ENVELOPE *));
-extern char            *xalloc __P((int));
-extern bool            sameaddr __P((ADDRESS *, ADDRESS *));
-extern FILE            *dfopen __P((char *, int, int));
-extern EVENT           *setevent __P((time_t, int(*)(), int));
-extern char            *sfgets __P((char *, int, FILE *, time_t, char *));
-extern char            *queuename __P((ENVELOPE *, int));
-extern time_t          curtime __P(());
-extern bool            transienterror __P((int));
+extern char    *xalloc __P((int));
+extern FILE    *dfopen __P((char *, int, int));
+extern char    *sfgets __P((char *, int, FILE *, time_t, char *));
+extern char    *queuename __P((ENVELOPE *, int));
+extern time_t  curtime __P(());
+extern bool    transienterror __P((int));
+extern char    *fgetfolded __P((char *, int, FILE *));
+extern char    *username __P(());
+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 void    commaize __P((HDR *, char *, bool, MCI *, ENVELOPE *));
+extern char    *hvalue __P((char *, HDR *));
+extern char    *defcharset __P((ENVELOPE *));
+extern bool    wordinclass __P((char *, int));
+extern char    *denlstring __P((char *, bool, bool));
+extern void    makelower __P((char *));
+extern void    rebuildaliases __P((MAP *, bool));
+extern void    readaliases __P((MAP *, FILE *, bool, bool));
+extern void    finis __P(());
+extern void    setsender __P((char *, ENVELOPE *, char **, bool));
+extern FILE    *safefopen __P((char *, int, int, int));
+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 void    disconnect __P((int, ENVELOPE *));
+extern void    putxline __P((char *, 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 const char      *errstring __P((int));
-extern void            expand __P((char *, char *, char *, ENVELOPE *));
-extern void            define __P((int, char *, ENVELOPE *));
-extern char            *macvalue __P((int, ENVELOPE *));
-extern char            **prescan __P((char *, int, char[], int, char **));
-extern int             rewrite __P((char **, int, int, ENVELOPE *));
-extern char            *fgetfolded __P((char *, int, FILE *));
-extern ADDRESS         *recipient __P((ADDRESS *, ADDRESS **, ENVELOPE *));
-extern ENVELOPE                *newenvelope __P((ENVELOPE *, ENVELOPE *));
-extern void            dropenvelope __P((ENVELOPE *));
-extern void            clearenvelope __P((ENVELOPE *, int));
-extern char            *username __P(());
-extern MCI             *mci_get __P((char *, MAILER *));
-extern char            *pintvl __P((time_t, int));
-extern char            *map_rewrite __P((MAP *, char *, int, char **));
-extern ADDRESS         *getctladdr __P((ADDRESS *));
-extern char            *anynet_ntoa __P((SOCKADDR *));
-extern char            *remotename __P((char *, MAILER *, int, int *, ENVELOPE *));
-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 sigfunc_t       setsignal __P((int, sigfunc_t));
-extern char            *shortenstring __P((char *, int));
-extern bool            usershellok __P((char *));
-extern void            commaize __P((HDR *, char *, int, MCI *, ENVELOPE *));
-extern char            *denlstring __P((char *, int, 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));
+
+#ifdef XDEBUG
+extern void            checkfd012 __P((char *));
+#endif
 
 /* ellipsis is a different case though */
 #ifdef __STDC__
@@ -972,3 +1225,13 @@ extern void               usrerr();
 extern void            message();
 extern void            nmessage();
 #endif
+
+#if !HASSNPRINTF
+# ifdef __STDC__
+extern void            snprintf(char *, size_t, const char *, ...);
+extern void            vsnprintf(char *, size_t, const char *, va_list);
+# else
+extern void            snprintf();
+extern void            vsnprintf();
+# endif
+#endif
index 142a7f5..14db22c 100644 (file)
@@ -1,9 +1,9 @@
 cpyr
-cpyr   Copyright (c) 1983  Eric P. Allman
+cpyr   Copyright (c) 1983, 1995  Eric P. Allman
 cpyr   Copyright (c) 1988, 1993
 cpyr       The Regents of the University of California.  All rights reserved.
 cpyr   
-cpyr   @(#)sendmail.hf 8.2 (Berkeley) 7/16/93
+cpyr   @(#)sendmail.hf 8.7 (Berkeley) 10/1/95
 cpyr
 smtp   Commands:
 smtp           HELO    EHLO    MAIL    RCPT    DATA
@@ -11,7 +11,7 @@ smtp          RSET    NOOP    QUIT    HELP    VRFY
 smtp           EXPN    VERB
 smtp   For more info use "HELP <topic>".
 smtp   To report bugs in the implementation send email to
-smtp           sendmail@CS.Berkeley.EDU.
+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.
@@ -32,7 +32,7 @@ quit  QUIT
 quit           Exit sendmail (SMTP).
 verb   VERB
 verb           Go into verbose mode.  This sends 0xy responses that are
-verb           are not RFC821 standard (but should be)  They are recognized
+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
@@ -56,3 +56,23 @@ saml         implementation.
 turn   TURN
 turn           Reverses the direction of the connection.  Not currently
 turn           implemented.
+-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'.
index eef525d..5e7ab64 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -36,9 +36,9 @@
 
 #ifndef lint
 #ifdef SMTP
-static char sccsid[] = "@(#)srvrsmtp.c 8.37 (Berkeley) 4/13/94 (with SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.97 (Berkeley) 11/18/95 (with SMTP)";
 #else
-static char sccsid[] = "@(#)srvrsmtp.c 8.37 (Berkeley) 4/13/94 (without SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.97 (Berkeley) 11/18/95 (without SMTP)";
 #endif
 #endif /* not lint */
 
@@ -117,35 +117,34 @@ bool      OneXact = FALSE;                /* one xaction only this run */
 char   *CurSmtpClient;                 /* who's at the other end of channel */
 
 static char    *skipword();
-extern char    RealUserName[];
 
 
 #define MAXBADCOMMANDS 25              /* maximum number of bad commands */
 
+void
 smtp(e)
-       register ENVELOPE *e;
+       register ENVELOPE *volatile e;
 {
        register char *p;
        register struct cmd *c;
        char *cmd;
        auto ADDRESS *vrfyqueue;
        ADDRESS *a;
-       bool gotmail;                   /* mail command received */
-       bool gothello;                  /* helo command received */
+       volatile bool gotmail;          /* mail command received */
+       volatile bool gothello;         /* helo command received */
        bool vrfy;                      /* set if this is a vrfy command */
-       char *protocol;                 /* sending protocol */
-       char *sendinghost;              /* sending hostname */
-       unsigned long msize;            /* approximate maximum message size */
-       char *peerhostname;             /* name of SMTP peer or "localhost" */
+       char *volatile protocol;        /* sending protocol */
+       char *volatile sendinghost;     /* sending hostname */
+       char *volatile peerhostname;    /* name of SMTP peer or "localhost" */
        auto char *delimptr;
        char *id;
-       int nrcpts;                     /* number of RCPT commands */
+       volatile int nrcpts = 0;        /* number of RCPT commands */
        bool doublequeue;
-       int badcommands = 0;            /* count of bad commands */
+       volatile int badcommands = 0;   /* count of bad commands */
        char inp[MAXLINE];
        char cmdbuf[MAXLINE];
-       extern char Version[];
        extern ENVELOPE BlankEnvelope;
+       extern void help __P((char *));
 
        if (fileno(OutChannel) != fileno(stdout))
        {
@@ -162,28 +161,41 @@ smtp(e)
                CurSmtpClient = CurHostName;
 
        setproctitle("server %s startup", CurSmtpClient);
-       expand("\201e", inp, &inp[sizeof inp], e);
-       if (BrokenSmtpPeers)
+#ifdef LOG
+       if (LogLevel > 11)
        {
-               p = strchr(inp, '\n');
-               if (p != NULL)
-                       *p = '\0';
-               message("220 %s", inp);
+               /* log connection information */
+               syslog(LOG_INFO, "SMTP connect from %.100s (%.100s)",
+                       CurSmtpClient, anynet_ntoa(&RealHostAddr));
        }
-       else
-       {
-               char *q = inp;
+#endif
 
-               while (q != NULL)
-               {
-                       p = strchr(q, '\n');
-                       if (p != NULL)
-                               *p++ = '\0';
-                       message("220-%s", q);
-                       q = p;
-               }
-               message("220 ESMTP spoken here");
+       /* 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;
@@ -213,7 +225,7 @@ smtp(e)
 
                /* read the input line */
                SmtpPhase = "server cmd read";
-               setproctitle("server %s cmd read", CurHostName);
+               setproctitle("server %s cmd read", CurSmtpClient);
                p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
                                SmtpPhase);
 
@@ -226,7 +238,7 @@ smtp(e)
                                MyHostName, CurSmtpClient);
 #ifdef LOG
                        if (LogLevel > (gotmail ? 1 : 19))
-                               syslog(LOG_NOTICE, "lost input channel from %s",
+                               syslog(LOG_NOTICE, "lost input channel from %.100s",
                                        CurSmtpClient);
 #endif
                        if (InChild)
@@ -285,6 +297,39 @@ smtp(e)
                                protocol = "SMTP";
                                SmtpPhase = "server HELO";
                        }
+
+                       /* check for valid domain name (re 1123 5.2.5) */
+                       if (*p == '\0')
+                       {
+                               message("501 %s requires domain address",
+                                       cmdbuf);
+                               break;
+                       }
+                       else
+                       {
+                               register char *q;
+
+                               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')
+                               {
+                                       message("501 Invalid domain name");
+                                       break;
+                               }
+                       }
+
                        sendinghost = newstr(p);
                        gothello = TRUE;
                        if (c->cmdcode != CMDEHLO)
@@ -300,10 +345,19 @@ smtp(e)
                                MyHostName, CurSmtpClient);
                        if (!bitset(PRIV_NOEXPN, PrivacyFlags))
                                message("250-EXPN");
+#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-VERB");
+                       message("250-ONEX");
                        message("250 HELP");
                        break;
 
@@ -344,7 +398,7 @@ smtp(e)
                        {
                                auth_warning(e,
                                        "Host %s didn't use HELO protocol",
-                                       peerhostname);
+                                       CurSmtpClient);
                        }
 #ifdef PICKY_HELO_CHECK
                        if (strcasecmp(sendinghost, peerhostname) != 0 &&
@@ -352,7 +406,7 @@ smtp(e)
                             strcasecmp(sendinghost, MyHostName) != 0))
                        {
                                auth_warning(e, "Host %s claimed to be %s",
-                                       peerhostname, sendinghost);
+                                       CurSmtpClient, sendinghost);
                        }
 #endif
 
@@ -362,7 +416,7 @@ smtp(e)
                        define('s', sendinghost, e);
                        initsys(e);
                        nrcpts = 0;
-                       e->e_flags |= EF_LOGSENDER;
+                       e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
                        setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
 
                        /* child -- go do the processing */
@@ -392,19 +446,21 @@ smtp(e)
 
                        /* check for possible spoofing */
                        if (RealUid != 0 && OpMode == MD_SMTP &&
-                           (e->e_from.q_mailer != LocalMailer &&
-                            strcmp(e->e_from.q_user, RealUserName) != 0))
+                           !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 */
-                       msize = 0;
+                       e->e_msgsize = 0;
                        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))
@@ -435,59 +491,17 @@ smtp(e)
                                        printf("MAIL: got arg %s=\"%s\"\n", kp,
                                                vp == NULL ? "<null>" : vp);
 
-                               if (strcasecmp(kp, "size") == 0)
-                               {
-                                       if (vp == NULL)
-                                       {
-                                               usrerr("501 SIZE requires a value");
-                                               /* NOTREACHED */
-                                       }
-# ifdef __STDC__
-                                       msize = strtoul(vp, (char **) NULL, 10);
-# else
-                                       msize = strtol(vp, (char **) NULL, 10);
-# endif
-                               }
-                               else if (strcasecmp(kp, "body") == 0)
-                               {
-                                       if (vp == NULL)
-                                       {
-                                               usrerr("501 BODY requires a value");
-                                               /* NOTREACHED */
-                                       }
-# ifdef MIME
-                                       if (strcasecmp(vp, "8bitmime") == 0)
-                                       {
-                                               e->e_bodytype = "8BITMIME";
-                                               SevenBit = FALSE;
-                                       }
-                                       else if (strcasecmp(vp, "7bit") == 0)
-                                       {
-                                               e->e_bodytype = "7BIT";
-                                               SevenBit = TRUE;
-                                       }
-                                       else
-                                       {
-                                               usrerr("501 Unknown BODY type %s",
-                                                       vp);
-                                       }
-# endif
-                               }
-                               else
-                               {
-                                       usrerr("501 %s parameter unrecognized", kp);
-                                       /* NOTREACHED */
-                               }
+                               mail_esmtp_args(kp, vp, e);
                        }
 
-                       if (MaxMessageSize > 0 && msize > MaxMessageSize)
+                       if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
                        {
                                usrerr("552 Message size exceeds fixed maximum message size (%ld)",
                                        MaxMessageSize);
                                /* NOTREACHED */
                        }
                                
-                       if (!enoughspace(msize))
+                       if (!enoughdiskspace(e->e_msgsize))
                        {
                                message("452 Insufficient disk space; try again later");
                                break;
@@ -517,11 +531,53 @@ smtp(e)
                        p = skipword(p, "to");
                        if (p == NULL)
                                break;
-                       a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
+                       a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
                        if (a == NULL)
                                break;
-                       a->q_flags |= QPRIMARY;
-                       a = recipient(a, &e->e_sendqueue, e);
+                       p = delimptr;
+
+                       /* now parse ESMTP arguments */
+                       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);
+                       }
+
+                       /* save in recipient list after ESMTP mods */
+                       a = recipient(a, &e->e_sendqueue, 0, e);
+
                        if (Errors != 0)
                                break;
 
@@ -575,10 +631,18 @@ smtp(e)
 
                        /* collect the text of the message */
                        SmtpPhase = "collect";
-                       collect(TRUE, doublequeue, e);
+                       buffer_errors();
+                       collect(InChannel, TRUE, doublequeue, NULL, e);
+                       flush_errors(TRUE);
                        if (Errors != 0)
                                goto abortmessage;
-                       HoldErrs = TRUE;
+
+                       /* 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.
@@ -599,42 +663,32 @@ smtp(e)
                        */
 
                        SmtpPhase = "delivery";
-                       if (nrcpts != 1 && !doublequeue)
-                       {
-                               HoldErrs = TRUE;
-                               e->e_errormode = EM_MAIL;
-                       }
                        e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
                        id = e->e_id;
 
-                       /* send to all recipients */
-                       sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
-                       e->e_to = NULL;
-
-                       /* issue success if appropriate and reset */
-                       if (Errors == 0 || HoldErrs)
-                               message("250 %s Message accepted for delivery", id);
-
-                       if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
+                       if (doublequeue)
                        {
-                               /* avoid sending back an extra message */
-                               e->e_flags &= ~EF_FATALERRS;
-                               e->e_flags |= EF_CLRQUEUE;
+                               /* make sure it is in the queue */
+                               queueup(e, FALSE);
                        }
                        else
                        {
-                               /* from now on, we have to operate silently */
-                               HoldErrs = TRUE;
-                               e->e_errormode = EM_MAIL;
+                               /* send to all recipients */
+                               sendall(e, SM_DEFAULT);
+                       }
+                       e->e_to = NULL;
 
-                               /* if we just queued, poke it */
-                               if (doublequeue && e->e_sendmode != SM_QUEUE)
-                               {
-                                       extern pid_t dowork();
+                       /* issue success message */
+                       message("250 %s Message accepted for delivery", id);
 
-                                       unlockqueue(e);
-                                       (void) dowork(id, TRUE, TRUE, e);
-                               }
+                       /* if we just queued, poke it */
+                       if (doublequeue && e->e_sendmode != SM_QUEUE &&
+                           e->e_sendmode != SM_DEFER)
+                       {
+                               extern pid_t dowork();
+
+                               unlockqueue(e);
+                               (void) dowork(id, TRUE, TRUE, e);
                        }
 
   abortmessage:
@@ -651,6 +705,9 @@ smtp(e)
 
                  case CMDRSET:         /* rset -- reset state */
                        message("250 Reset state");
+
+                       /* arrange to ignore any current send list */
+                       e->e_sendqueue = NULL;
                        e->e_flags |= EF_CLRQUEUE;
                        if (InChild)
                                finis();
@@ -668,13 +725,14 @@ smtp(e)
                                                PrivacyFlags))
                        {
                                if (vrfy)
-                                       message("252 Who's to say?");
+                                       message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
                                else
                                        message("502 Sorry, we do not allow this operation");
 #ifdef LOG
                                if (LogLevel > 5)
-                                       syslog(LOG_INFO, "%s: %s [rejected]",
-                                               CurSmtpClient, inp);
+                                       syslog(LOG_INFO, "%.100s: %s [rejected]",
+                                               CurSmtpClient,
+                                               shortenstring(inp, 203));
 #endif
                                break;
                        }
@@ -689,14 +747,16 @@ smtp(e)
                                break;
 #ifdef LOG
                        if (LogLevel > 5)
-                               syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
+                               syslog(LOG_INFO, "%.100s: %s",
+                                       CurSmtpClient,
+                                       shortenstring(inp, 203));
 #endif
                        vrfyqueue = NULL;
                        QuickAbort = TRUE;
                        if (vrfy)
                                e->e_flags |= EF_VRFYONLY;
                        while (*p != '\0' && isascii(*p) && isspace(*p))
-                               *p++;
+                               p++;
                        if (*p == '\0')
                        {
                                message("501 Argument required");
@@ -704,7 +764,7 @@ smtp(e)
                        }
                        else
                        {
-                               (void) sendtolist(p, NULLADDR, &vrfyqueue, e);
+                               (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
                        }
                        if (Errors != 0)
                        {
@@ -718,6 +778,8 @@ smtp(e)
                        }
                        while (vrfyqueue != NULL)
                        {
+                               extern void printvrfyaddr __P((ADDRESS *, bool));
+
                                a = vrfyqueue;
                                while ((a = a->q_next) != NULL &&
                                       bitset(QDONTSEND|QBADADDR, a->q_flags))
@@ -742,6 +804,9 @@ smtp(e)
                        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);
 
@@ -786,8 +851,8 @@ doquit:
 # ifdef LOG
                        if (LogLevel > 0)
                                syslog(LOG_CRIT,
-                                   "\"%s\" command from %s (%s)",
-                                   c->cmdname, peerhostname,
+                                   "\"%s\" command from %.100s (%.100s)",
+                                   c->cmdname, CurSmtpClient,
                                    anynet_ntoa(&RealHostAddr));
 # endif
                        /* FALL THROUGH */
@@ -847,7 +912,7 @@ skipword(p, w)
        {
          syntax:
                message("501 Syntax error in parameters scanning \"%s\"",
-                       firstp);
+                       shortenstring(firstp, 203));
                Errors++;
                return (NULL);
        }
@@ -865,6 +930,183 @@ skipword(p, w)
        return (p);
 }
 \f/*
+**  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 */
+       }
+}
+\f/*
+**  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 */
+       }
+}
+\f/*
 **  PRINTVRFYADDR -- print an entry in the verify queue
 **
 **     Parameters:
@@ -878,6 +1120,7 @@ skipword(p, w)
 **             Prints the appropriate 250 codes.
 */
 
+void
 printvrfyaddr(a, last)
        register ADDRESS *a;
        bool last;
@@ -905,66 +1148,6 @@ printvrfyaddr(a, last)
        }
 }
 \f/*
-**  HELP -- implement the HELP command.
-**
-**     Parameters:
-**             topic -- the topic we want help for.
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             outputs the help file to message output.
-*/
-
-help(topic)
-       char *topic;
-{
-       register FILE *hf;
-       int len;
-       char buf[MAXLINE];
-       bool noinfo;
-
-       if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
-       {
-               /* no help */
-               errno = 0;
-               message("502 HELP not implemented");
-               return;
-       }
-
-       if (topic == NULL || *topic == '\0')
-               topic = "smtp";
-       else
-               makelower(topic);
-
-       len = strlen(topic);
-       noinfo = TRUE;
-
-       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 unknown");
-       else
-               message("214 End of HELP info");
-       (void) fclose(hf);
-}
-\f/*
 **  RUNINCHILD -- return twice -- once in the child, then in the parent again
 **
 **     Parameters:
@@ -978,6 +1161,7 @@ help(topic)
 **             none.
 */
 
+int
 runinchild(label, e)
        char *label;
        register ENVELOPE *e;
@@ -989,7 +1173,7 @@ runinchild(label, e)
                childpid = dofork();
                if (childpid < 0)
                {
-                       syserr("%s: cannot fork", label);
+                       syserr("451 %s: cannot fork", label);
                        return (1);
                }
                if (childpid > 0)
@@ -997,12 +1181,12 @@ runinchild(label, e)
                        auto int st;
 
                        /* parent -- wait for child to complete */
-                       setproctitle("server %s child wait", CurHostName);
+                       setproctitle("server %s child wait", CurSmtpClient);
                        st = waitfor(childpid);
                        if (st == -1)
-                               syserr("%s: lost child", label);
+                               syserr("451 %s: lost child", label);
                        else if (!WIFEXITED(st))
-                               syserr("%s: died on signal %d",
+                               syserr("451 %s: died on signal %d",
                                        label, st & 0177);
 
                        /* if we exited on a QUIT command, complete the process */
@@ -1030,3 +1214,72 @@ runinchild(label, e)
 }
 
 # endif /* SMTP */
+\f/*
+**  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;
+       char buf[MAXLINE];
+       extern char Version[];
+
+
+       if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == 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);
+}
index 07893e5..cfb02c6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)stab.c     8.1 (Berkeley) 6/7/93";
+static char sccsid[] = "@(#)stab.c     8.6 (Berkeley) 8/31/95";
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -57,7 +57,7 @@ static char sccsid[] = "@(#)stab.c    8.1 (Berkeley) 6/7/93";
 **             can update the symbol table.
 */
 
-# define STABSIZE      400
+# define STABSIZE      2003
 
 static STAB    *SymTab[STABSIZE];
 
@@ -78,20 +78,28 @@ stab(name, type, op)
 
        /*
        **  Compute the hashing function
-       **
-       **      We could probably do better....
        */
 
        hfunc = type;
        for (p = name; *p != '\0'; p++)
-               hfunc = (((hfunc << 7) | lower(*p)) & 077777) % STABSIZE;
+               hfunc = ((hfunc << 1) ^ (lower(*p) & 0377)) % STABSIZE;
 
        if (tTd(36, 9))
                printf("(hfunc=%d) ", hfunc);
 
        ps = &SymTab[hfunc];
-       while ((s = *ps) != NULL && (strcasecmp(name, s->s_name) || s->s_type != type))
-               ps = &s->s_next;
+       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.
@@ -125,7 +133,6 @@ stab(name, type, op)
        s = (STAB *) xalloc(sizeof *s);
        bzero((char *) s, sizeof *s);
        s->s_name = newstr(name);
-       makelower(s->s_name);
        s->s_type = type;
 
        /* link it in */
@@ -157,7 +164,7 @@ stabapply(func, arg)
        {
                for (s = *shead; s != NULL; s = s->s_next)
                {
-                       if (tTd(38, 90))
+                       if (tTd(36, 90))
                                printf("stabapply: trying %d/%s\n",
                                        s->s_type, s->s_name);
                        func(s, arg);
index 2dc6827..f59885a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)stats.c    8.3 (Berkeley) 8/28/93";
+static char sccsid[] = "@(#)stats.c    8.5 (Berkeley) 5/28/95";
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -49,6 +49,7 @@ bool  GotStats = FALSE;       /* set when we have stats to merge */
 **  MARKSTATS -- mark statistics
 */
 
+void
 markstats(e, to)
        register ENVELOPE *e;
        register ADDRESS *to;
@@ -82,6 +83,7 @@ markstats(e, to)
 **             merges the Stat structure with the sfile file.
 */
 
+void
 poststats(sfile)
        char *sfile;
 {
index fff3783..ad4f1e1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)sysexits.c 8.1 (Berkeley) 6/7/93";
+static char sccsid[] = "@(#)sysexits.c 8.5 (Berkeley) 5/24/95";
 #endif /* not lint */
 
-#include <sysexits.h>
+#include <sendmail.h>
 
 /*
 **  SYSEXITS.C -- error messages corresponding to sysexits.h
@@ -65,3 +65,120 @@ char *SysExMsg[] =
 };
 
 int N_SysEx = sizeof(SysExMsg) / sizeof(SysExMsg[0]);
+\f/*
+**  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;
+}
index 29421ee..9762cab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)trace.c    8.2 (Berkeley) 3/14/94";
+static char sccsid[] = "@(#)trace.c    8.4 (Berkeley) 5/28/95";
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -57,6 +57,7 @@ u_char                *tTvect;
 int            tTsize;
 static char    *DefFlags;
 
+void
 tTsetup(vect, size, defflags)
        u_char *vect;
        int size;
@@ -79,6 +80,7 @@ tTsetup(vect, size, defflags)
 **             sets/clears trace flags.
 */
 
+void
 tTflag(s)
        register char *s;
 {
index 7887cb3..1bcf68c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
 #include "sendmail.h"
 
 #ifndef lint
-#ifdef USERDB
-static char sccsid [] = "@(#)udb.c     8.8 (Berkeley) 4/14/94 (with USERDB)";
+#if USERDB
+static char sccsid [] = "@(#)udb.c     8.33 (Berkeley) 11/29/95 (with USERDB)";
 #else
-static char sccsid [] = "@(#)udb.c     8.8 (Berkeley) 4/14/94 (without USERDB)";
+static char sccsid [] = "@(#)udb.c     8.33 (Berkeley) 11/29/95 (without USERDB)";
 #endif
 #endif
 
-#ifdef USERDB
+#if USERDB
 
 #include <errno.h>
-#include <netdb.h>
-#include <db.h>
+
+#ifdef NEWDB
+# include <db.h>
+#else
+# define DBT   struct _data_base_thang_
+DBT
+{
+       void    *data;          /* pointer to data */
+       size_t  size;           /* length of data */
+};
+#endif
 
 #ifdef HESIOD
-#include <hesiod.h>
+# include <hesiod.h>
 #endif /* HESIOD */
 
 /*
@@ -82,6 +91,7 @@ struct udbent
                } udb_forward;
 #define udb_fwdhost    udb_u.udb_forward._udb_fwdhost
 
+#ifdef NEWDB
                /* type UE_FETCH -- lookup in local database */
                struct
                {
@@ -90,6 +100,7 @@ struct udbent
                } udb_lookup;
 #define udb_dbname     udb_u.udb_lookup._udb_dbname
 #define udb_dbp                udb_u.udb_lookup._udb_dbp
+#endif
        } udb_u;
 };
 
@@ -114,6 +125,8 @@ struct option
 **     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
@@ -132,13 +145,13 @@ int               UdbSock = -1;
 bool           UdbInitialized = FALSE;
 
 int
-udbexpand(a, sendq, e)
+udbexpand(a, sendq, aliaslevel, e)
        register ADDRESS *a;
        ADDRESS **sendq;
+       int aliaslevel;
        register ENVELOPE *e;
 {
        int i;
-       register char *p;
        DBT key;
        DBT info;
        bool breakout;
@@ -201,6 +214,7 @@ udbexpand(a, sendq, e)
 
                switch (up->udb_type)
                {
+#ifdef NEWDB
                  case UDB_DBFETCH:
                        key.data = keybuf;
                        key.size = keylen;
@@ -227,7 +241,6 @@ udbexpand(a, sendq, e)
                                if (bitset(EF_VRFYONLY, e->e_flags))
                                {
                                        a->q_flags |= QVERIFIED;
-                                       e->e_nrcpts++;
                                        return EX_OK;
                                }
 
@@ -242,12 +255,11 @@ udbexpand(a, sendq, e)
                                message("expanded to %s", user);
 #ifdef LOG
                                if (LogLevel >= 10)
-                                       syslog(LOG_INFO, "%s: expand %s => %s",
-                                               e->e_id, e->e_to, user);
+                                       syslog(LOG_INFO, "%s: expand %.100s => %s",
+                                               e->e_id, e->e_to,
+                                               shortenstring(user, 203));
 #endif
-                               AliasLevel++;
-                               naddrs += sendtolist(user, a, sendq, e);
-                               AliasLevel--;
+                               naddrs += sendtolist(user, a, sendq, aliaslevel + 1, e);
 
                                if (user != buf)
                                        free(user);
@@ -299,9 +311,11 @@ udbexpand(a, sendq, e)
                                fprintf(e->e_xfp,
                                        "Message delivered to mailing list %s\n",
                                        a->q_paddr);
-                               e->e_flags |= EF_SENDRECEIPT;
                        }
+                       e->e_flags |= EF_SENDRECEIPT;
+                       a->q_flags |= QDELIVERED|QEXPANDED;
                        break;
+#endif
 
 #ifdef HESIOD
                  case UDB_HESIOD:
@@ -312,12 +326,45 @@ udbexpand(a, sendq, e)
                                        keybuf, keylen);
                        /* look up the key via hesiod */
                        i = hes_udb_get(&key, &info);
-                       if (i > 0 || info.size <= 0)
+                       if (i < 0)
                        {
+                               syserr("udbexpand: hesiod-get %.*s stat %d",
+                                       key.size, 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",
-                                       keybuf, keylen);
+                                       printf("udbexpand: no match on %s (%d)\n",
+                                               keybuf, 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());
+                                       continue;
+                               }
+                               sprintf(info.data, "%s@%s",
+                                       hp->po_name, hp->po_host);
+                               info.size = strlen(info.data);
+#else
                                continue;
+#endif
                        }
                        if (tTd(28, 80))
                                printf("udbexpand: match %.*s: %.*s\n",
@@ -327,8 +374,6 @@ udbexpand(a, sendq, e)
                        if (bitset(EF_VRFYONLY, e->e_flags))
                        {
                                a->q_flags |= QVERIFIED;
-                               e->e_nrcpts++;
-                               free(info.data);
                                return EX_OK;
                        }
 
@@ -339,17 +384,15 @@ udbexpand(a, sendq, e)
                                user = xalloc(info.size + 1);
                        bcopy(info.data, user, info.size);
                        user[info.size] = '\0';
-                       free(info.data);
 
                        message("hesioded to %s", user);
 #ifdef LOG
                        if (LogLevel >= 10)
-                               syslog(LOG_INFO, "%s: hesiod %s => %s",
-                                       e->e_id, e->e_to, user);
+                               syslog(LOG_INFO, "%s: hesiod %.100s => %s",
+                                       e->e_id, e->e_to,
+                                       shortenstring(user, 203));
 #endif
-                       AliasLevel++;
-                       naddrs = sendtolist(user, a, sendq, e);
-                       AliasLevel--;
+                       naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
 
                        if (user != buf)
                                free(user);
@@ -363,12 +406,6 @@ udbexpand(a, sendq, e)
                                }
                                a->q_flags |= QDONTSEND;
                        }
-                       if (i < 0)
-                       {
-                               syserr("udbexpand: hesiod-get %.*s stat %d",
-                                       key.size, key.data, i);
-                               return EX_TEMPFAIL;
-                       }
 
                        /*
                        **  If this address has a -request address, reflect
@@ -386,7 +423,6 @@ udbexpand(a, sendq, e)
                        a->q_owner = xalloc(info.size + 1);
                        bcopy(info.data, a->q_owner, info.size);
                        a->q_owner[info.size] = '\0';
-                       free(info.data);
                        break;
 #endif /* HESIOD */
 
@@ -405,9 +441,7 @@ udbexpand(a, sendq, e)
                        (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost);
                        message("expanded to %s", user);
                        a->q_flags &= ~QSELFREF;
-                       AliasLevel++;
-                       naddrs = sendtolist(user, a, sendq, e);
-                       AliasLevel--;
+                       naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e);
                        if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
                        {
                                if (tTd(28, 5))
@@ -509,6 +543,7 @@ udbmatch(user, field)
 
                switch (up->udb_type)
                {
+#ifdef NEWDB
                  case UDB_DBFETCH:
                        key.data = keybuf;
                        key.size = keylen;
@@ -528,6 +563,7 @@ udbmatch(user, field)
                                printf("udbmatch ==> %s\n", p);
                        return p;
                        break;
+#endif
 
 #ifdef HESIOD
                  case UDB_HESIOD:
@@ -545,11 +581,9 @@ udbmatch(user, field)
                        p = xalloc(info.size + 1);
                        bcopy(info.data, p, info.size);
                        p[info.size] = '\0';
-                       free(info.data);
                        if (tTd(28, 1))
                                printf("udbmatch ==> %s\n", p);
                        return p;
-                       break;
 #endif /* HESIOD */
                }
        }
@@ -572,6 +606,7 @@ udbmatch(user, field)
        {
                switch (up->udb_type)
                {
+#ifdef NEWDB
                  case UDB_DBFETCH:
                        /* get the default case for this database */
                        if (up->udb_default == NULL)
@@ -613,6 +648,7 @@ udbmatch(user, field)
                                printf("udbmatch ==> %s\n", p);
                        return p;
                        break;
+#endif
 
 #ifdef HESIOD
                  case UDB_HESIOD:
@@ -634,7 +670,6 @@ udbmatch(user, field)
                                up->udb_default = xalloc(info.size + 1);
                                bcopy(info.data, up->udb_default, info.size);
                                up->udb_default[info.size] = '\0';
-                               free(info.data);
                        }
                        else if (up->udb_default[0] == '\0')
                                continue;
@@ -649,7 +684,6 @@ udbmatch(user, field)
                                continue;
                        }
 
-                       free(info.data);
                        /* they exist -- build the actual address */
                        p = xalloc(strlen(user) + strlen(up->udb_default) + 2);
                        (void) strcpy(p, user);
@@ -667,6 +701,57 @@ udbmatch(user, field)
        return NULL;
 }
 \f/*
+**  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.
+*/
+
+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);
+}
+\f/*
 **  _UDBX_INIT -- parse the UDB specification, opening any valid entries.
 **
 **     Parameters:
@@ -688,9 +773,7 @@ int
 _udbx_init()
 {
        register char *p;
-       int i;
        register struct udbent *up;
-       char buf[BUFSIZ];
 
        if (UdbInitialized)
                return EX_OK;
@@ -705,11 +788,14 @@ _udbx_init()
        while (p != NULL)
        {
                char *spec;
-               auto int rcode;
                int nopts;
+# if 0
+               auto int rcode;
                int nmx;
+               int i;
                register struct hostent *h;
                char *mxhosts[MAXMXHOSTS + 1];
+# endif
                struct option opts[MAXUDBOPTS + 1];
 
                while (*p == ' ' || *p == '\t' || *p == ',')
@@ -743,10 +829,13 @@ _udbx_init()
                **                      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 == '*')
@@ -776,14 +865,14 @@ _udbx_init()
 
                        for (i = 0; i < nmx; i++)
                        {
-                               h = gethostbyname(mxhosts[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,
-                                     sizeof up->udb_addr.sin_addr);
+                                     INADDRSZ);
                                up->udb_addr.sin_port = UdbPort;
                                up->udb_timeout = UdbTimeout;
                                up++;
@@ -796,6 +885,7 @@ _udbx_init()
                                (void) fcntl(UdbSock, F_SETFD, 1);
                        }
                        break;
+#endif
 
                  case '@':     /* forward to remote host */
                        up->udb_type = UDB_FORWARD;
@@ -803,22 +893,31 @@ _udbx_init()
                        up++;
                        break;
 
+#ifdef HESIOD
                  case 'h':     /* use hesiod */
                  case 'H':
-#ifdef HESIOD
                        if (strcasecmp(spec, "hesiod") != 0)
-                               break;
+                               goto badspec;
                        up->udb_type = UDB_HESIOD;
                        up++;
-#endif /* HESIOD */
                        break;
+#endif /* HESIOD */
 
+#ifdef NEWDB
                  case '/':     /* look up remote name */
                        up->udb_dbname = spec;
                        errno = 0;
                        up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL);
                        if (up->udb_dbp == NULL)
                        {
+                               if (tTd(28, 1))
+                               {
+                                       int saveerrno = errno;
+
+                                       printf("dbopen(%s): %s",
+                                               spec, errstring(errno));
+                                       errno = saveerrno;
+                               }
                                if (errno != ENOENT && errno != EACCES)
                                {
 #ifdef LOG
@@ -834,6 +933,12 @@ _udbx_init()
                        up->udb_type = UDB_DBFETCH;
                        up++;
                        break;
+#endif
+
+                 default:
+badspec:
+                       syserr("Unknown UDB spec %s", spec);
+                       break;
                }
        }
        up->udb_type = UDB_EOLIST;
@@ -844,15 +949,21 @@ _udbx_init()
                {
                        switch (up->udb_type)
                        {
+#ifdef 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:
@@ -880,6 +991,7 @@ _udbx_init()
        */
 
   tempfail:
+#ifdef NEWDB
        for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
        {
                if (up->udb_type == UDB_DBFETCH)
@@ -887,6 +999,7 @@ _udbx_init()
                        (*up->udb_dbp->close)(up->udb_dbp);
                }
        }
+#endif
        return EX_TEMPFAIL;
 }
 
@@ -929,20 +1042,24 @@ hes_udb_get(key, info)
 {
        char *name, *type;
        char *p, **hp;
+       char kbuf[MAXKEY + 1];
 
-       name = key->data;
-       type = strchr(name, ':');
+       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 */
        hp = hes_resolve(name, type);
-       if (hp == NULL)
+       *--type = ':';
+       if (hp == NULL || hp[0] == NULL)
        {
                /* network problem or timeout */
                if (hes_error() == HES_ER_NET)
@@ -954,19 +1071,19 @@ hes_udb_get(key, info)
        {
                /*
                **  If there are multiple matches, just return the
-               **  first one and free the others.
+               **  first one.
                **
                **  XXX These should really be returned; for example,
                **  XXX it is legal for :maildrop to be multi-valued.
                */
 
-               for (p = hp[1]; p; p++)
-                       free(p);
-
                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 */
@@ -974,9 +1091,10 @@ hes_udb_get(key, info)
 #else /* not USERDB */
 
 int
-udbexpand(a, sendq, e)
+udbexpand(a, sendq, aliaslevel, e)
        ADDRESS *a;
        ADDRESS **sendq;
+       int aliaslevel;
        ENVELOPE *e;
 {
        return EX_OK;
index ba33a79..8f9009e 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     @(#)useful.h    8.2 (Berkeley) 9/24/93
+ *     @(#)useful.h    8.4 (Berkeley) 4/21/95
  */
 
 # include <sys/types.h>
 
 /* support for bool type */
-typedef char   bool;
+typedef int    bool;
 # define TRUE  1
 # define FALSE 0
 
index 06acd3f..d33bd5e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -36,9 +36,9 @@
 
 #ifndef lint
 #ifdef SMTP
-static char sccsid[] = "@(#)usersmtp.c 8.18 (Berkeley) 1/24/94 (with SMTP)";
+static char sccsid[] = "@(#)usersmtp.c 8.65 (Berkeley) 9/28/95 (with SMTP)";
 #else
-static char sccsid[] = "@(#)usersmtp.c 8.18 (Berkeley) 1/24/94 (without SMTP)";
+static char sccsid[] = "@(#)usersmtp.c 8.65 (Berkeley) 9/28/95 (without SMTP)";
 #endif
 #endif /* not lint */
 
@@ -63,9 +63,7 @@ char  SmtpError[MAXLINE] = "";        /* save failure error messages */
 int    SmtpPid;                        /* pid of mailer */
 bool   SmtpNeedIntro;                  /* need "while talking" in transcript */
 
-#ifdef __STDC__
-extern smtpmessage(char *f, MAILER *m, MCI *mci, ...);
-#endif
+extern void    smtpmessage __P((char *f, MAILER *m, MCI *mci, ...));
 \f/*
 **  SMTPINIT -- initialize SMTP.
 **
@@ -83,6 +81,7 @@ extern        smtpmessage(char *f, MAILER *m, MCI *mci, ...);
 **             creates connection and sends initial protocol.
 */
 
+void
 smtpinit(m, mci, e)
        struct mailer *m;
        register MCI *mci;
@@ -105,6 +104,8 @@ smtpinit(m, mci, e)
 
        SmtpError[0] = '\0';
        CurHostName = mci->mci_host;            /* XXX UGLY XXX */
+       if (CurHostName == NULL)
+               CurHostName = MyHostName;
        SmtpNeedIntro = TRUE;
        switch (mci->mci_state)
        {
@@ -186,18 +187,16 @@ tryhelo:
        **  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.
-       **
-       **      If this code remains at all, "CheckLoopBack" should be
-       **      a mailer flag.  This is a MAYBENEXTRELEASE feature.
        */
 
        p = strchr(&SmtpReplyBuffer[4], ' ');
        if (p != NULL)
                *p = '\0';
-       if (CheckLoopBack && strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
+       if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
+           strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
        {
-               syserr("553 %s config error: mail loops back to myself",
-                       MyHostName);
+               syserr("553 %s config error: mail loops back to me (MX problem?)",
+                       mci->mci_host);
                mci->mci_exitstat = EX_CONFIG;
                mci->mci_errno = 0;
                smtpquit(m, mci, e);
@@ -244,9 +243,9 @@ tryhelo:
 \f/*
 **  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.
@@ -256,23 +255,24 @@ tryhelo:
 */
 
 void
-esmtp_check(line, m, mci, e)
+esmtp_check(line, firstline, m, mci, e)
        char *line;
+       bool firstline;
        MAILER *m;
        register MCI *mci;
        ENVELOPE *e;
 {
-       if (strlen(line) < 5)
-               return;
-       line += 4;
-       if (strncmp(line, "ESMTP ", 6) == 0)
+       if (strstr(line, "ESMTP") != NULL)
                mci->mci_flags |= MCIF_ESMTP;
+       if (strstr(line, "8BIT-OK") != NULL)
+               mci->mci_flags |= MCIF_8BITOK;
 }
 \f/*
 **  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.
@@ -282,15 +282,19 @@ esmtp_check(line, m, mci, e)
 */
 
 void
-helo_options(line, m, mci, e)
+helo_options(line, firstline, m, mci, e)
        char *line;
+       bool firstline;
        MAILER *m;
        register MCI *mci;
        ENVELOPE *e;
 {
        register char *p;
 
-       if (strlen(line) < 5)
+       if (firstline)
+               return;
+
+       if (strlen(line) < (SIZE_T) 5)
                return;
        line += 4;
        p = strchr(line, ' ');
@@ -309,6 +313,8 @@ helo_options(line, m, mci, e)
        }
        else if (strcasecmp(line, "expn") == 0)
                mci->mci_flags |= MCIF_EXPN;
+       else if (strcasecmp(line, "dsn") == 0)
+               mci->mci_flags |= MCIF_DSN;
 }
 \f/*
 **  SMTPMAILFROM -- send MAIL command
@@ -319,6 +325,7 @@ helo_options(line, m, mci, e)
 **             e -- the envelope (including the sender to specify).
 */
 
+int
 smtpmailfrom(m, mci, e)
        struct mailer *m;
        MCI *mci;
@@ -326,7 +333,8 @@ smtpmailfrom(m, mci, e)
 {
        int r;
        char *bufp;
-       char buf[MAXNAME];
+       char *bodytype;
+       char buf[MAXNAME + 1];
        char optbuf[MAXLINE];
 
        if (tTd(18, 2))
@@ -338,6 +346,64 @@ smtpmailfrom(m, mci, e)
        else
                strcpy(optbuf, "");
 
+       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)
+               {
+                       strcat(optbuf, " BODY=");
+                       strcat(optbuf, bodytype);
+               }
+       }
+       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 */
+               usrerr("%s does not support 8BITMIME", mci->mci_host);
+               mci->mci_status = "5.6.3";
+               return EX_DATAERR;
+       }
+
+       if (bitset(MCIF_DSN, mci->mci_flags))
+       {
+               if (e->e_envid != NULL)
+               {
+                       strcat(optbuf, " ENVID=");
+                       strcat(optbuf, e->e_envid);
+               }
+
+               /* RET= parameter */
+               if (bitset(EF_RET_PARAM, e->e_flags))
+               {
+                       strcat(optbuf, " RET=");
+                       if (bitset(EF_NO_BODY_RETN, e->e_flags))
+                               strcat(optbuf, "HDRS");
+                       else
+                               strcat(optbuf, "FULL");
+               }
+       }
+
        /*
        **  Send the MAIL command.
        **      Designates the sender.
@@ -349,7 +415,7 @@ smtpmailfrom(m, mci, e)
            !bitnset(M_NO_NULL_FROM, m->m_flags))
                (void) strcpy(buf, "");
        else
-               expand("\201g", buf, &buf[sizeof buf - 1], e);
+               expand("\201g", buf, sizeof buf, e);
        if (buf[0] == '<')
        {
                /* strip off <angle brackets> (put back on below) */
@@ -360,7 +426,7 @@ smtpmailfrom(m, mci, e)
        }
        else
                bufp = buf;
-       if (e->e_from.q_mailer == LocalMailer ||
+       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);
@@ -373,37 +439,57 @@ smtpmailfrom(m, mci, e)
        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 || REPLYTYPE(r) == 4)
+       if (r < 0 || r == 421)
        {
+               /* communications failure/service shutting down */
                mci->mci_exitstat = EX_TEMPFAIL;
                mci->mci_errno = errno;
                smtpquit(m, mci, e);
                return EX_TEMPFAIL;
        }
-       else if (r == 250)
+       else if (REPLYTYPE(r) == 4)
+       {
+               return EX_TEMPFAIL;
+       }
+       else if (REPLYTYPE(r) == 2)
        {
-               mci->mci_exitstat = EX_OK;
                return EX_OK;
        }
+       else if (r == 501)
+       {
+               /* syntax error in arguments */
+               mci->mci_status = "5.5.2";
+               return EX_DATAERR;
+       }
+       else if (r == 553)
+       {
+               /* mailbox name not allowed */
+               mci->mci_status = "5.1.3";
+               return EX_DATAERR;
+       }
        else if (r == 552)
        {
-               /* signal service unavailable */
-               mci->mci_exitstat = EX_UNAVAILABLE;
-               smtpquit(m, mci, e);
+               /* exceeded storage allocation */
+               mci->mci_status = "5.2.2";
+               return EX_UNAVAILABLE;
+       }
+       else if (REPLYTYPE(r) == 5)
+       {
+               /* unknown error */
                return EX_UNAVAILABLE;
        }
 
 #ifdef LOG
        if (LogLevel > 1)
        {
-               syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s",
-                       e->e_id, SmtpReplyBuffer);
+               syslog(LOG_CRIT, "%s: %.100s: SMTP MAIL protocol error: %s",
+                       e->e_id, mci->mci_host,
+                       shortenstring(SmtpReplyBuffer, 403));
        }
 #endif
 
        /* protocol error -- close up */
        smtpquit(m, mci, e);
-       mci->mci_exitstat = EX_PROTOCOL;
        return EX_PROTOCOL;
 }
 \f/*
@@ -422,6 +508,7 @@ smtpmailfrom(m, mci, e)
 **             Sends the mail via SMTP.
 */
 
+int
 smtprcpt(to, m, mci, e)
        ADDRESS *to;
        register MAILER *m;
@@ -429,30 +516,77 @@ smtprcpt(to, m, mci, e)
        ENVELOPE *e;
 {
        register int r;
+       char optbuf[MAXLINE];
+       extern char *smtptodsn();
+
+       strcpy(optbuf, "");
+       if (bitset(MCIF_DSN, mci->mci_flags))
+       {
+               /* NOTIFY= parameter */
+               if (bitset(QHASNOTIFY, to->q_flags) &&
+                   bitset(QPRIMARY, to->q_flags))
+               {
+                       bool firstone = TRUE;
 
-       smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
+                       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");
+               }
+
+               /* ORCPT= parameter */
+               if (to->q_orcpt != NULL)
+               {
+                       strcat(optbuf, " ORCPT=");
+                       strcat(optbuf, to->q_orcpt);
+               }
+       }
+
+       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);
+               return EX_TEMPFAIL;
        else if (REPLYTYPE(r) == 2)
-               return (EX_OK);
+               return EX_OK;
        else if (r == 550 || r == 551 || r == 553)
-               return (EX_NOUSER);
-       else if (r == 552 || r == 554)
-               return (EX_UNAVAILABLE);
+               return EX_NOUSER;
+       else if (REPLYTYPE(r) == 5)
+               return EX_UNAVAILABLE;
 
 #ifdef LOG
        if (LogLevel > 1)
        {
-               syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s",
-                       e->e_id, SmtpReplyBuffer);
+               syslog(LOG_CRIT, "%s: %.100s: SMTP RCPT protocol error: %s",
+                       e->e_id, mci->mci_host,
+                       shortenstring(SmtpReplyBuffer, 403));
        }
 #endif
 
-       return (EX_PROTOCOL);
+       return EX_PROTOCOL;
 }
 \f/*
 **  SMTPDATA -- send the data and clean up the transaction.
@@ -469,8 +603,9 @@ smtprcpt(to, m, mci, e)
 */
 
 static jmp_buf CtxDataTimeout;
-static int     datatimeout();
+static void    datatimeout();
 
+int
 smtpdata(m, mci, e)
        struct mailer *m;
        register MCI *mci;
@@ -496,20 +631,21 @@ smtpdata(m, mci, e)
        if (r < 0 || REPLYTYPE(r) == 4)
        {
                smtpquit(m, mci, e);
-               return (EX_TEMPFAIL);
+               return EX_TEMPFAIL;
        }
-       else if (r == 554)
+       else if (REPLYTYPE(r) == 5)
        {
                smtprset(m, mci, e);
-               return (EX_UNAVAILABLE);
+               return EX_UNAVAILABLE;
        }
        else if (r != 354)
        {
 #ifdef LOG
                if (LogLevel > 1)
                {
-                       syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s",
-                               e->e_id, SmtpReplyBuffer);
+                       syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-1 protocol error: %s",
+                               e->e_id, mci->mci_host,
+                               shortenstring(SmtpReplyBuffer, 403));
                }
 #endif
                smtprset(m, mci, e);
@@ -533,16 +669,22 @@ smtpdata(m, mci, e)
        }
 
        timeout = e->e_msgsize / 16;
-       if (timeout < (time_t) 60)
-               timeout = (time_t) 60;
-       timeout += e->e_nrcpts * 90;
+       if (timeout < (time_t) 600)
+               timeout = (time_t) 600;
+       timeout += e->e_nrcpts * 300;
        ev = setevent(timeout, datatimeout, 0);
 
-       /* now output the actual message */
-       (*e->e_puthdr)(mci, e);
-       putline("\n", mci);
+       /*
+       **  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))
@@ -569,28 +711,31 @@ smtpdata(m, mci, e)
        if (r < 0)
        {
                smtpquit(m, mci, e);
-               return (EX_TEMPFAIL);
+               return EX_TEMPFAIL;
        }
        mci->mci_state = MCIS_OPEN;
        e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
        if (REPLYTYPE(r) == 4)
-               return (EX_TEMPFAIL);
-       else if (r == 250)
-               return (EX_OK);
-       else if (r == 552 || r == 554)
-               return (EX_UNAVAILABLE);
+               return EX_TEMPFAIL;
+       else if (REPLYCLASS(r) != 5)
+               /* fall through */ ;
+       else if (REPLYTYPE(r) == 2)
+               return EX_OK;
+       else if (REPLYTYPE(r) == 5)
+               return EX_UNAVAILABLE;
 #ifdef LOG
        if (LogLevel > 1)
        {
-               syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s",
-                       e->e_id, SmtpReplyBuffer);
+               syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-2 protocol error: %s",
+                       e->e_id, mci->mci_host,
+                       shortenstring(SmtpReplyBuffer, 403));
        }
 #endif
-       return (EX_PROTOCOL);
+       return EX_PROTOCOL;
 }
 
 
-static int
+static void
 datatimeout()
 {
        longjmp(CtxDataTimeout, 1);
@@ -608,6 +753,7 @@ datatimeout()
 **             sends the final protocol and closes the connection.
 */
 
+void
 smtpquit(m, mci, e)
        register MAILER *m;
        register MCI *mci;
@@ -647,6 +793,7 @@ smtpquit(m, mci, e)
 **  SMTPRSET -- send a RSET (reset) command
 */
 
+void
 smtprset(m, mci, e)
        register MAILER *m;
        register MCI *mci;
@@ -670,6 +817,7 @@ smtprset(m, mci, e)
 **  SMTPPROBE -- check the connection state
 */
 
+int
 smtpprobe(mci)
        register MCI *mci;
 {
@@ -693,9 +841,8 @@ smtpprobe(mci)
 **             mci -- the mailer connection info structure.
 **             e -- the current envelope.
 **             timeout -- the timeout for reads.
-**             pfunc -- processing function for second and subsequent
-**                     lines of response -- if null, no special
-**                     processing is done.
+**             pfunc -- processing function called on each line of response.
+**                     If null, no special processing is done.
 **
 **     Returns:
 **             reply code it reads.
@@ -704,6 +851,7 @@ smtpprobe(mci)
 **             flushes the mail file.
 */
 
+int
 reply(m, mci, e, timeout, pfunc)
        MAILER *m;
        MCI *mci;
@@ -749,7 +897,6 @@ reply(m, mci, e, timeout, pfunc)
                if (p == NULL)
                {
                        bool oldholderrs;
-                       extern char MsgBuf[];           /* err.c */
 
                        /* if the remote end closed early, fake an error */
                        if (errno == 0)
@@ -770,16 +917,18 @@ reply(m, mci, e, timeout, pfunc)
                                pause();
                        mci->mci_state = MCIS_ERROR;
                        smtpquit(m, mci, e);
-#ifdef XDEBUG
+#if XDEBUG
                        {
                                char wbuf[MAXLINE];
                                char *p = wbuf;
+
                                if (e->e_to != NULL)
                                {
-                                       sprintf(p, "%s... ", e->e_to);
+                                       sprintf(p, "%s... ",
+                                               shortenstring(e->e_to, 203));
                                        p += strlen(p);
                                }
-                               sprintf(p, "reply(%s) during %s",
+                               sprintf(p, "reply(%.100s) during %s",
                                        mci->mci_host, SmtpPhase);
                                checkfd012(wbuf);
                        }
@@ -815,8 +964,8 @@ reply(m, mci, e, timeout, pfunc)
                        nmessage("050 %s", bufp);
 
                /* process the line */
-               if (pfunc != NULL && !firstline)
-                       (*pfunc)(bufp, m, mci, e);
+               if (pfunc != NULL)
+                       (*pfunc)(bufp, firstline, m, mci, e);
 
                firstline = FALSE;
 
@@ -871,6 +1020,7 @@ reply(m, mci, e, timeout, pfunc)
 */
 
 /*VARARGS1*/
+void
 #ifdef __STDC__
 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
 #else
@@ -884,7 +1034,7 @@ smtpmessage(f, m, mci, va_alist)
        VA_LOCAL_DECL
 
        VA_START(mci);
-       (void) vsprintf(SmtpMsgBuffer, f, ap);
+       (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
        VA_END;
 
        if (tTd(18, 1) || Verbose)
index 3f6e182..0685bf4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)util.c     8.39.1.5 (Berkeley) 3/5/95";
+static char sccsid[] = "@(#)util.c     8.84 (Berkeley) 11/18/95";
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -57,6 +57,7 @@ static char sccsid[] = "@(#)util.c    8.39.1.5 (Berkeley) 3/5/95";
 **             deliver
 */
 
+void
 stripquotes(s)
        char *s;
 {
@@ -107,8 +108,7 @@ xalloc(sz)
        p = malloc((unsigned) sz);
        if (p == NULL)
        {
-               syserr("Out of memory!!");
-               abort();
+               syserr("!Out of memory!!");
                /* exit(EX_UNAVAILABLE); */
        }
        return (p);
@@ -208,6 +208,7 @@ copyqueue(addr)
 **             prints av.
 */
 
+void
 printav(av)
        register char **av;
 {
@@ -253,8 +254,9 @@ lower(c)
 **             output to stdout
 */
 
+void
 xputs(s)
-       register char *s;
+       register const char *s;
 {
        register int c;
        register struct metamac *mp;
@@ -269,11 +271,20 @@ xputs(s)
        {
                if (!isascii(c))
                {
-                       if (c == MATCHREPL || c == MACROEXPAND)
+                       if (c == MATCHREPL)
                        {
                                putchar('$');
                                continue;
                        }
+                       if (c == MACROEXPAND)
+                       {
+                               putchar('$');
+                               if (strchr("=~&?", *s) != NULL)
+                                       putchar(*s++);
+                               if (bitset(0200, *s))
+                                       printf("{%s}", macname(*s++ & 0377));
+                               continue;
+                       }
                        for (mp = MetaMacros; mp->metaname != '\0'; mp++)
                        {
                                if ((mp->metaval & 0377) == c)
@@ -282,6 +293,12 @@ xputs(s)
                                        break;
                                }
                        }
+                       if (c == MATCHCLASS || c == MATCHNCLASS)
+                       {
+                               if (!bitset(0200, *s))
+                                       continue;
+                               printf("{%s}", macname(*s++ & 0377));
+                       }
                        if (mp->metaname != '\0')
                                continue;
                        (void) putchar('\\');
@@ -296,9 +313,6 @@ xputs(s)
                /* wasn't a meta-macro -- find another way to print it */
                switch (c)
                {
-                 case '\0':
-                       continue;
-
                  case '\n':
                        c = 'n';
                        break;
@@ -316,6 +330,8 @@ xputs(s)
                        (void) putchar(c ^ 0100);
                        continue;
                }
+               (void) putchar('\\');
+               (void) putchar(c);
        }
        (void) fflush(stdout);
 }
@@ -336,6 +352,7 @@ xputs(s)
 **             parse
 */
 
+void
 makelower(p)
        register char *p;
 {
@@ -365,6 +382,7 @@ makelower(p)
 **             none.
 */
 
+void
 buildfname(gecos, login, buf)
        register char *gecos;
        char *login;
@@ -415,6 +433,8 @@ buildfname(gecos, login, buf)
 **                     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.
@@ -438,104 +458,195 @@ buildfname(gecos, login, buf)
 # define S_IXUSR       (S_IEXEC)
 #endif
 
+#define ST_MODE_NOFILE 0171147         /* unlikely to occur */
+
 int
-safefile(fn, uid, gid, uname, flags, mode)
+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;
        struct stat stbuf;
+       struct stat fstbuf;
 
-       if (tTd(54, 4))
+       if (tTd(44, 4))
                printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
                        fn, uid, gid, flags, mode);
        errno = 0;
+       if (st == NULL)
+               st = &fstbuf;
 
-       for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
+       /* 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
        {
-               *p = '\0';
-               if (stat(fn, &stbuf) < 0)
-                       break;
-               if (uid == 0 && !bitset(SFF_ROOTOK, flags))
+               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)
+#endif
                {
-                       if (bitset(S_IXOTH, stbuf.st_mode))
-                               continue;
-                       break;
+                       uid = st->st_uid;
+                       uname = NULL;
                }
-               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 &&
-                   ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
-                    (gr = getgrgid(stbuf.st_gid)) != NULL))
-               {
-                       register char **gp;
+#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;
+       }
 
-                       for (gp = gr->gr_mem; *gp != NULL; gp++)
-                               if (strcmp(*gp, uname) == 0)
-                                       break;
-                       if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode))
+       if (!bitset(SFF_NOPATHCHECK, flags) ||
+           (uid == 0 && !bitset(SFF_ROOTOK, flags)))
+       {
+               /* check the path to the file for acceptability */
+               for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
+               {
+                       *p = '\0';
+                       if (stat(fn, &stbuf) < 0)
+                               break;
+                       if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode))
+                               message("051 WARNING: writable directory %s",
+                                       fn);
+                       if (uid == 0 && !bitset(SFF_ROOTOK, flags))
+                       {
+                               if (bitset(S_IXOTH, stbuf.st_mode))
+                                       continue;
+                               break;
+                       }
+                       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 &&
+                           ((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))
-                       break;
+                       if (!bitset(S_IXOTH, stbuf.st_mode))
+                               break;
+               }
+               if (p != NULL)
+               {
+                       int ret = errno;
+
+                       if (ret == 0)
+                               ret = EACCES;
+                       if (tTd(44, 4))
+                               printf("\t[dir %s] %s\n", fn, errstring(ret));
+                       *p = '/';
+                       return ret;
+               }
        }
-       if (p != NULL)
-       {
-               int ret = errno;
 
-               if (ret == 0)
-                       ret = EACCES;
-               if (tTd(54, 4))
-                       printf("\t[dir %s] %s\n", fn, errstring(ret));
-               *p = '/';
-               return ret;
-       }
+       /*
+       **  If the target file doesn't exist, check the directory to
+       **  ensure that it is writable by this user.
+       */
 
-#ifdef HASLSTAT
-       if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf)
-                                       : stat(fn, &stbuf)) < 0)
-#else
-       if (stat(fn, &stbuf) < 0)
-#endif
+       if (file_errno != 0)
        {
-               int ret = errno;
+               int ret = file_errno;
 
-               if (tTd(54, 4))
+               if (tTd(44, 4))
                        printf("\t%s\n", errstring(ret));
 
                errno = 0;
+               if (!bitset(SFF_CREAT, flags))
+                       return ret;
+
+               /* check to see if legal to create the file */
+               p = strrchr(fn, '/');
+               if (p == NULL)
+                       return ENOTDIR;
+               *p = '\0';
+               if (stat(fn, &stbuf) >= 0)
+               {
+                       int md = S_IWRITE|S_IEXEC;
+                       if (stbuf.st_uid != uid)
+                               md >>= 6;
+                       if ((stbuf.st_mode & md) != md)
+                               errno = EACCES;
+               }
+               ret = errno;
+               if (tTd(44, 4))
+                       printf("\t[final dir %s uid %d mode %o] %s\n",
+                               fn, stbuf.st_uid, stbuf.st_mode,
+                               errstring(ret));
+               *p = '/';
+               st->st_mode = ST_MODE_NOFILE;
                return ret;
        }
 
 #ifdef S_ISLNK
-       if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
+       if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
        {
-               if (tTd(54, 4))
-                       printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode);
+               if (tTd(44, 4))
+                       printf("\t[slink mode %o]\tEPERM\n", st->st_mode);
                return EPERM;
        }
 #endif
+       if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
+       {
+               if (tTd(44, 4))
+                       printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode);
+               return EPERM;
+       }
+       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 %o]\tEPERM]\n", st->st_mode);
+               return EPERM;
+       }
 
        if (uid == 0 && !bitset(SFF_ROOTOK, flags))
                mode >>= 6;
-       else if (stbuf.st_uid != uid)
+       else if (st->st_uid != uid)
        {
                mode >>= 3;
-               if (stbuf.st_gid == gid)
+               if (st->st_gid == gid)
                        ;
 #ifndef NO_GROUP_SET
                else if (uname != NULL &&
-                        ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
-                         (gr = getgrgid(stbuf.st_gid)) != NULL))
+                        ((gr != NULL && gr->gr_gid == st->st_gid) ||
+                         (gr = getgrgid(st->st_gid)) != NULL))
                {
                        register char **gp;
 
@@ -549,22 +660,104 @@ safefile(fn, uid, gid, uname, flags, mode)
                else
                        mode >>= 3;
        }
-       if (tTd(54, 4))
+       if (tTd(44, 4))
                printf("\t[uid %d, stat %o, mode %o] ",
-                       stbuf.st_uid, stbuf.st_mode, mode);
-       if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
+                       st->st_uid, st->st_mode, mode);
+       if ((st->st_uid == uid || st->st_uid == 0 ||
             !bitset(SFF_MUSTOWN, flags)) &&
-           (stbuf.st_mode & mode) == mode)
+           (st->st_mode & mode) == mode)
        {
-               if (tTd(54, 4))
+               if (tTd(44, 4))
                        printf("\tOK\n");
                return 0;
        }
-       if (tTd(54, 4))
+       if (tTd(44, 4))
                printf("\tEACCES\n");
        return EACCES;
 }
 \f/*
+**  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.
+*/
+
+#ifndef O_ACCMODE
+# define O_ACCMODE     (O_RDONLY|O_WRONLY|O_RDWR)
+#endif
+
+FILE *
+safefopen(fn, omode, cmode, sff)
+       char *fn;
+       int omode;
+       int cmode;
+       int sff;
+{
+       int rval;
+       FILE *fp;
+       int smode;
+       struct stat stb, sta;
+
+       if (bitset(O_CREAT, omode))
+               sff |= SFF_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, 0, 0, NULL, sff, smode, &stb);
+       else
+               rval = safefile(fn, RealUid, RealGid, RealUserName,
+                               sff, smode, &stb);
+       if (rval != 0)
+       {
+               errno = rval;
+               return NULL;
+       }
+       if (stb.st_mode == ST_MODE_NOFILE)
+               omode |= O_EXCL;
+
+       fp = dfopen(fn, omode, cmode);
+       if (fp == NULL)
+               return NULL;
+       if (bitset(O_EXCL, omode))
+               return fp;
+       if (fstat(fileno(fp), &sta) < 0 ||
+           sta.st_nlink != stb.st_nlink ||
+           sta.st_dev != stb.st_dev ||
+           sta.st_ino != stb.st_ino ||
+           sta.st_uid != stb.st_uid ||
+           sta.st_gid != stb.st_gid)
+       {
+               syserr("554 cannot open: file %s changed after open", fn);
+               fclose(fp);
+               errno = EPERM;
+               return NULL;
+       }
+       return fp;
+}
+\f/*
 **  FIXCRLF -- fix <CR><LF> in line.
 **
 **     Looks for the <CR><LF> combination and turns it into the
@@ -583,6 +776,7 @@ safefile(fn, uid, gid, uname, flags, mode)
 **             line is changed in place.
 */
 
+void
 fixcrlf(line, stripnl)
        char *line;
        bool stripnl;
@@ -607,10 +801,6 @@ fixcrlf(line, stripnl)
 **     whatever), so this tries to get around it.
 */
 
-#ifndef O_ACCMODE
-# define O_ACCMODE     (O_RDONLY|O_WRONLY|O_RDWR)
-#endif
-
 struct omodes
 {
        int     mask;
@@ -693,16 +883,46 @@ dfopen(filename, omode, cmode)
 **             output of l to fp.
 */
 
+void
 putline(l, mci)
        register char *l;
        register MCI *mci;
+{
+       putxline(l, mci, PXLF_MAPFROM);
+}
+\f/*
+**  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.
+**             mci -- the mailer connection information.
+**             pxflags -- flag bits:
+**                 PXLF_MAPFROM -- map From_ to >From_.
+**                 PXLF_STRIP8BIT -- strip 8th bit.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             output of l to fp.
+*/
+
+void
+putxline(l, mci, pxflags)
+       register char *l;
+       register MCI *mci;
+       int pxflags;
 {
        register char *p;
        register char svchar;
        int slop = 0;
 
        /* strip out 0200 bits -- these can look like TELNET protocol */
-       if (bitset(MCIF_7BIT, mci->mci_flags))
+       if (bitset(MCIF_7BIT, mci->mci_flags) ||
+           bitset(PXLF_STRIP8BIT, pxflags))
        {
                for (p = l; (svchar = *p) != '\0'; ++p)
                        if (bitset(0200, svchar))
@@ -734,6 +954,15 @@ putline(l, mci)
                                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 (TrafficLogFile != NULL)
+                                       (void) putc('>', TrafficLogFile);
+                       }
                        fputs(l, mci->mci_out);
                        (void) putc('!', mci->mci_out);
                        fputs(mci->mci_mailer->m_eol, mci->mci_out);
@@ -776,6 +1005,7 @@ putline(l, mci)
 **             f is unlinked.
 */
 
+void
 xunlink(f)
        char *f;
 {
@@ -806,13 +1036,14 @@ xunlink(f)
 **             fp is closed.
 */
 
+void
 xfclose(fp, a, b)
        FILE *fp;
        char *a, *b;
 {
        if (tTd(53, 99))
                printf("xfclose(%x) %s %s\n", fp, a, b);
-#ifdef XDEBUG
+#if XDEBUG
        if (fileno(fp) == 1)
                syserr("xfclose(%s %s): fd = 1", a, b);
 #endif
@@ -839,10 +1070,7 @@ xfclose(fp, a, b)
 */
 
 static jmp_buf CtxReadTimeout;
-static int     readtimeout();
-static EVENT   *GlobalTimeout = NULL;
-static bool    EnableTimeout = FALSE;
-static int     ReadProgress;
+static void    readtimeout();
 
 char *
 sfgets(buf, siz, fp, timeout, during)
@@ -868,22 +1096,19 @@ sfgets(buf, siz, fp, timeout, during)
                {
 # ifdef LOG
                        syslog(LOG_NOTICE,
-                           "timeout waiting for input from %s during %s\n",
+                           "timeout waiting for input from %.100s during %s",
                            CurHostName? CurHostName: "local", during);
 # endif
                        errno = 0;
                        usrerr("451 timeout waiting for input during %s",
                                during);
                        buf[0] = '\0';
-#ifdef XDEBUG
+#if XDEBUG
                        checkfd012(during);
 #endif
                        return (NULL);
                }
-               if (GlobalTimeout == NULL)
-                       ev = setevent(timeout, readtimeout, 0);
-               else
-                       EnableTimeout = TRUE;
+               ev = setevent(timeout, readtimeout, 0);
        }
 
        /* try to read */
@@ -898,10 +1123,7 @@ sfgets(buf, siz, fp, timeout, during)
        }
 
        /* clear the event if it has not sprung */
-       if (GlobalTimeout == NULL)
-               clrevent(ev);
-       else
-               EnableTimeout = FALSE;
+       clrevent(ev);
 
        /* clean up the books and exit */
        LineNumber++;
@@ -914,50 +1136,30 @@ sfgets(buf, siz, fp, timeout, during)
        }
        if (TrafficLogFile != NULL)
                fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
-       if (SevenBit)
+       if (SevenBitInput)
+       {
                for (p = buf; *p != '\0'; p++)
                        *p &= ~0200;
-       return (buf);
-}
-
-void
-sfgetset(timeout)
-       time_t timeout;
-{
-       /* cancel pending timer */
-       if (GlobalTimeout != NULL)
-       {
-               clrevent(GlobalTimeout);
-               GlobalTimeout = NULL;
        }
-
-       /* schedule fresh one if so requested */
-       if (timeout != 0)
+       else if (!HasEightBits)
        {
-               ReadProgress = LineNumber;
-               GlobalTimeout = setevent(timeout, readtimeout, timeout);
+               for (p = buf; *p != '\0'; p++)
+               {
+                       if (bitset(0200, *p))
+                       {
+                               HasEightBits = TRUE;
+                               break;
+                       }
+               }
        }
+       return (buf);
 }
 
-static
+static void
 readtimeout(timeout)
        time_t timeout;
 {
-       /* terminate if ordinary timeout */
-       if (GlobalTimeout == NULL)
-               longjmp(CtxReadTimeout, 1);
-
-       /* terminate if no progress was made -- reset state */
-       if (EnableTimeout && (LineNumber <= ReadProgress))
-       {
-               EnableTimeout = FALSE;
-               GlobalTimeout = NULL;
-               longjmp(CtxReadTimeout, 2);
-       }
-
-       /* schedule a new timeout */
-       GlobalTimeout = NULL;
-       sfgetset(timeout);
+       longjmp(CtxReadTimeout, 1);
 }
 \f/*
 **  FGETFOLDED -- like fgets, but know about folded lines.
@@ -1033,7 +1235,9 @@ fgetfolded(buf, n, f)
        }
        if (p == bp)
                return (NULL);
-       *--p = '\0';
+       if (p[-1] == '\n')
+               p--;
+       *p = '\0';
        return (bp);
 }
 \f/*
@@ -1095,6 +1299,7 @@ atobool(s)
 **             none.
 */
 
+int
 atooct(s)
        register char *s;
 {
@@ -1241,10 +1446,11 @@ strcontainedin(a, b)
 **             none
 */
 
+void
 checkfd012(where)
        char *where;
 {
-#ifdef XDEBUG
+#if XDEBUG
        register int i;
        struct stat stbuf;
 
@@ -1277,9 +1483,9 @@ checkfd012(where)
 **             none.
 */
 
-#include <netdb.h>
 #include <arpa/inet.h>
 
+void
 printopenfds(logit)
        bool logit;
 {
@@ -1299,18 +1505,22 @@ printopenfds(logit)
 **             logit -- if set, send output to syslog instead of stdout.
 */
 
+void
 dumpfd(fd, printclosed, logit)
        int fd;
        bool printclosed;
        bool logit;
 {
-       register struct hostent *hp;
        register char *p;
+       char *hp;
        char *fmtstr;
-       struct sockaddr_in sin;
+#ifdef S_IFSOCK
+       SOCKADDR sa;
+#endif
        auto int slen;
        struct stat st;
        char buf[200];
+       extern char *hostnamebyanyaddr();
 
        p = buf;
        sprintf(p, "%3d: ", fd);
@@ -1341,26 +1551,30 @@ dumpfd(fd, printclosed, logit)
          case S_IFSOCK:
                sprintf(p, "SOCK ");
                p += strlen(p);
-               slen = sizeof sin;
-               if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
-                       sprintf(p, "(badsock)");
+               slen = sizeof sa;
+               if (getsockname(fd, &sa.sa, &slen) < 0)
+                       sprintf(p, "(%s)", errstring(errno));
                else
                {
-                       hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
-                       sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
-                                                  : hp->h_name, ntohs(sin.sin_port));
+                       hp = hostnamebyanyaddr(&sa);
+                       if (sa.sa.sa_family == AF_INET)
+                               sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
+                       else
+                               sprintf(p, "%s", hp);
                }
                p += strlen(p);
                sprintf(p, "->");
                p += strlen(p);
-               slen = sizeof sin;
-               if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
-                       sprintf(p, "(badsock)");
+               slen = sizeof sa;
+               if (getpeername(fd, &sa.sa, &slen) < 0)
+                       sprintf(p, "(%s)", errstring(errno));
                else
                {
-                       hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
-                       sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
-                                                  : hp->h_name, ntohs(sin.sin_port));
+                       hp = hostnamebyanyaddr(&sa);
+                       if (sa.sa.sa_family == AF_INET)
+                               sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
+                       else
+                               sprintf(p, "%s", hp);
                }
                break;
 #endif
@@ -1411,7 +1625,7 @@ defprint:
 printit:
 #ifdef LOG
        if (logit)
-               syslog(LOG_DEBUG, "%s", buf);
+               syslog(LOG_DEBUG, "%.800s", buf);
        else
 #endif
                printf("%s\n", buf);
@@ -1436,7 +1650,7 @@ printit:
 
 char *
 shortenstring(s, m)
-       register char *s;
+       register const char *s;
        int m;
 {
        int l;
@@ -1444,7 +1658,7 @@ shortenstring(s, m)
 
        l = strlen(s);
        if (l < m)
-               return s;
+               return (char *) s;
        if (m > MAXSHORTSTR)
                m = MAXSHORTSTR;
        else if (m < 10)
@@ -1466,6 +1680,240 @@ shortenstring(s, m)
        return buf;
 }
 \f/*
+**  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';
+}
+\f/*
+**  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();
+       setgid(DefGid);
+       setuid(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);
+}
+\f/*
+**  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.
+**
+**     Returns:
+**             buf if successful.
+**             NULL otherwise.
+*/
+
+char *
+get_column(line, col, delim, buf)
+       char line[];
+       int col;
+       char delim;
+       char buf[];
+{
+       char *p;
+       char *begin, *end;
+       int i;
+       char delimbuf[3];
+       
+       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 && 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 && isspace(*begin))
+                               begin++;
+               }
+       }
+       
+       end = strpbrk(begin, delimbuf);
+       if (end == NULL)
+       {
+               strcpy(buf, begin);
+       }
+       else
+       {
+               strncpy(buf, begin, end - begin);
+               buf[end - begin] = '\0';
+       }
+       return buf;
+}
+\f/*
 **  CLEANSTRCPY -- copy string keeping out bogus characters
 **
 **     Parameters:
@@ -1517,8 +1965,8 @@ cleanstrcpy(t, f, l)
 char *
 denlstring(s, strict, logattacks)
        char *s;
-       int strict;
-       int logattacks;
+       bool strict;
+       bool logattacks;
 {
        register char *p;
        int l;
@@ -1545,14 +1993,16 @@ denlstring(s, strict, logattacks)
        for (p = bp; (p = strchr(p, '\n')) != NULL; )
                *p++ = ' ';
 
+/*
 #ifdef LOG
        if (logattacks)
        {
-               syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"",
+               syslog(LOG_NOTICE, "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
                        RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
-                       shortenstring(bp, 80));
+                       shortenstring(bp, 203));
        }
 #endif
+*/
 
        return bp;
 }
index ad4dd52..644f1a2 100644 (file)
@@ -33,7 +33,7 @@
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)version.c  8.6.12.1 (Berkeley) 3/28/95";
+static char sccsid[] = "@(#)version.c  8.7.3.1 (Berkeley) 12/3/95";
 #endif /* not lint */
 
-char   Version[] = "8.6.12";
+char   Version[] = "8.7.3";
index 37a1bb2..133f28b 100644 (file)
@@ -20,6 +20,7 @@ HP-UX 9.01    OK      93.11.19  Cassidy (on 7xx series)
 
 Solaris        2.1
 Solaris        2.2     FAIL    93.07.19  Bill Wisner
+Solaris        2.3     FAIL    95.11.22  Scott J. Kramer <sjk@lux.com>
 
 OSF/1  T1.3-4  OK      93.07.19  eric (on DEC Alpha)
 OSF/1  1.3     OK      94.12.10  Jeff A. Earickson (on Intel Paragon)
@@ -45,9 +46,25 @@ Linux        0.99p10 OK      93.08.08  Karl London
 Linux  0.99p13 OK      93.09.27  Christian Kuhtz
 Linux  0.99p14 OK      93.11.30  Christian Kuhtz <chk@data-hh.Hanse.DE>
 Linux  1.0     OK      94.03.19  Shayne Smith <snsmith@rastus.brisnet.org.au>
+Linux  1.2.13  OK      95.11.02  Sven Neuhaus <sven@ping.de>
 
 BSD/386        1.0     OK      93.11.13  Tony Sanders
 
 DELL   2.2     OK      93.11.15  Peter Wemm (using -DSETEUID)
 
 Pyramid        5.0d    OK      95.01.14  David Miller <davem@nadzieja.rutgers.edu>
+
+
+
+The following are results of running t_seteuid on various architectures.
+
+OPSYS  VERSION STATUS  DATE      TESTER/NOTES
+=====  ======= ======  ====      ============
+
+Solaris        2.3     OK      95.11.22  Scott J. Kramer <sjk@lux.com>
+Solaris        2.4     OK      95.09.22  Thomas 'Mike' Michlmayr <mike@cosy.sbg.ac.at>
+
+Linux  1.2.13  FAIL    95.11.02  Sven Neuhaus <sven@ping.de>
+
+IRIX   5.2     OK      95.12.01  Mark Andrews <mandrews@aw.sgi.com>
+IRIX   5.3     OK      95.12.01  Mark Andrews <mandrews@aw.sgi.com>
diff --git a/usr.sbin/sendmail/test/t_seteuid.c b/usr.sbin/sendmail/test/t_seteuid.c
new file mode 100644 (file)
index 0000000..4dfe3d6
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+**  This program checks to see if your version of seteuid works.
+**  Compile it, make it setuid root, and run it as yourself (NOT as
+**  root).  If it won't compile or outputs any MAYDAY messages, don't
+**  define USESETEUID in conf.h.
+**
+**     NOTE:  It is not sufficient to have seteuid in your library.
+**     You must also have saved uids that function properly.
+**
+**  Compilation is trivial -- just "cc t_seteuid.c".  Make it setuid,
+**  root and then execute it as a non-root user.
+*/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#ifdef __hpux
+#define seteuid(e)     setresuid(-1, e, -1)
+#endif
+
+main()
+{
+       int fail = 0;
+       uid_t realuid = getuid();
+
+       printuids("initial uids", realuid, 0);
+
+       if (geteuid() != 0)
+       {
+               printf("SETUP ERROR: re-run setuid root\n");
+               exit(1);
+       }
+
+       if (getuid() == 0)
+       {
+               printf("SETUP ERROR: must be run by a non-root user\n");
+               exit(1);
+       }
+
+       if (seteuid(1) < 0)
+               printf("seteuid(1) failure\n");
+       printuids("after seteuid(1)", realuid, 1);
+
+       if (geteuid() != 1)
+       {
+               fail++;
+               printf("MAYDAY!  Wrong effective uid\n");
+       }
+
+       /* do activity here */
+
+       if (seteuid(0) < 0)
+       {
+               fail++;
+               printf("seteuid(0) failure\n");
+       }
+       printuids("after seteuid(0)", realuid, 0);
+
+       if (geteuid() != 0)
+       {
+               fail++;
+               printf("MAYDAY!  Wrong effective uid\n");
+       }
+       if (getuid() != realuid)
+       {
+               fail++;
+               printf("MAYDAY!  Wrong real uid\n");
+       }
+       printf("\n");
+
+       if (seteuid(2) < 0)
+       {
+               fail++;
+               printf("seteuid(2) failure\n");
+       }
+       printuids("after seteuid(2)", realuid, 2);
+
+       if (geteuid() != 2)
+       {
+               fail++;
+               printf("MAYDAY!  Wrong effective uid\n");
+       }
+
+       /* do activity here */
+
+       if (seteuid(0) < 0)
+       {
+               fail++;
+               printf("seteuid(0) failure\n");
+       }
+       printuids("after seteuid(0)", realuid, 0);
+
+       if (geteuid() != 0)
+       {
+               fail++;
+               printf("MAYDAY!  Wrong effective uid\n");
+       }
+       if (getuid() != realuid)
+       {
+               fail++;
+               printf("MAYDAY!  Wrong real uid\n");
+       }
+
+       if (fail)
+       {
+               printf("\nThis system cannot use seteuid\n");
+               exit(1);
+       }
+
+       exit(0);
+}
+
+printuids(str, r, e)
+       char *str;
+       int r, e;
+{
+       printf("%s (should be %d/%d): r/euid=%d/%d\n", str, r, e,
+               getuid(), geteuid());
+}
index 2d087c3..ca9a06f 100644 (file)
@@ -4,7 +4,8 @@
 **  root).  If it won't compile or outputs any MAYDAY messages, don't
 **  define HASSETREUID in conf.h.
 **
-**  Compilation is trivial -- just "cc t_setreuid.c".
+**  Compilation is trivial -- just "cc t_setreuid.c".  Make it setuid,
+**  root and then execute it as a non-root user.
 */
 
 #include <sys/types.h>
 
 main()
 {
+       int fail = 0;
        uid_t realuid = getuid();
 
        printuids("initial uids", realuid, 0);
 
        if (geteuid() != 0)
        {
-               printf("re-run setuid root\n");
+               printf("SETUP ERROR: re-run setuid root\n");
+               exit(1);
+       }
+
+       if (getuid() == 0)
+       {
+               printf("SETUP ERROR: must be run by a non-root user\n");
                exit(1);
        }
 
        if (setreuid(0, 1) < 0)
+       {
+               fail++;
                printf("setreuid(0, 1) failure\n");
+       }
        printuids("after setreuid(0, 1)", 0, 1);
 
        if (geteuid() != 1)
+       {
+               fail++;
                printf("MAYDAY!  Wrong effective uid\n");
+       }
 
        /* do activity here */
 
        if (setreuid(-1, 0) < 0)
+       {
+               fail++;
                printf("setreuid(-1, 0) failure\n");
+       }
        printuids("after setreuid(-1, 0)", 0, 0);
        if (setreuid(realuid, 0) < 0)
+       {
+               fail++;
                printf("setreuid(%d, 0) failure\n", realuid);
+       }
        printuids("after setreuid(realuid, 0)", realuid, 0);
 
        if (geteuid() != 0)
+       {
+               fail++;
                printf("MAYDAY!  Wrong effective uid\n");
+       }
        if (getuid() != realuid)
+       {
+               fail++;
                printf("MAYDAY!  Wrong real uid\n");
+       }
        printf("\n");
 
        if (setreuid(0, 2) < 0)
+       {
+               fail++;
                printf("setreuid(0, 2) failure\n");
+       }
        printuids("after setreuid(0, 2)", 0, 2);
 
        if (geteuid() != 2)
+       {
+               fail++;
                printf("MAYDAY!  Wrong effective uid\n");
+       }
 
        /* do activity here */
 
        if (setreuid(-1, 0) < 0)
+       {
+               fail++;
                printf("setreuid(-1, 0) failure\n");
+       }
        printuids("after setreuid(-1, 0)", 0, 0);
        if (setreuid(realuid, 0) < 0)
+       {
+               fail++;
                printf("setreuid(%d, 0) failure\n", realuid);
+       }
        printuids("after setreuid(realuid, 0)", realuid, 0);
 
        if (geteuid() != 0)
+       {
+               fail++;
                printf("MAYDAY!  Wrong effective uid\n");
+       }
        if (getuid() != realuid)
+       {
+               fail++;
                printf("MAYDAY!  Wrong real uid\n");
+       }
+
+       if (fail)
+       {
+               printf("\nThis system cannot use setreuid\n");
+               exit(1);
+       }
 
        exit(0);
 }