Sendmail source for the 3B1/7300 (part 3/8)
David H. Brierley
dave at galaxia.Newport.RI.US
Sat Feb 25 12:28:16 AEST 1989
----- cut here and feed to /bin/sh -----
#! /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 archive 3 (of 8)."
# Contents: src/conf.c src/envelope.c src/recipient.c src/savemail.c
# Wrapped by dave at galaxia on Fri Feb 24 20:23:49 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/conf.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/conf.c'\"
else
echo shar: Extracting \"'src/conf.c'\" \(12327 characters\)
sed "s/^X//" >'src/conf.c' <<'END_OF_FILE'
X/*
X** Sendmail
X** Copyright (c) 1983 Eric P. Allman
X** Berkeley, California
X**
X** Copyright (c) 1983 Regents of the University of California.
X** All rights reserved. The Berkeley software License Agreement
X** specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char SccsId[] = "@(#)conf.c 5.14 (Berkeley) 1/10/86";
X#endif not lint
X
X# include <pwd.h>
X# include <sys/ioctl.h>
X# ifdef sun
X# include <sys/param.h>
X# endif sun
X# include "sendmail.h"
X
X/*
X** CONF.C -- Sendmail Configuration Tables.
X**
X** Defines the configuration of this installation.
X**
X** Compilation Flags:
X** V6 -- running on a version 6 system. This determines
X** whether to define certain routines between
X** the two systems. If you are running a funny
X** system, e.g., V6 with long tty names, this
X** should be checked carefully.
X** VMUNIX -- running on a Berkeley UNIX system.
X**
X** Configuration Variables:
X** HdrInfo -- a table describing well-known header fields.
X** Each entry has the field name and some flags,
X** which are described in sendmail.h.
X**
X** Notes:
X** I have tried to put almost all the reasonable
X** configuration information into the configuration
X** file read at runtime. My intent is that anything
X** here is a function of the version of UNIX you
X** are running, or is really static -- for example
X** the headers are a superset of widely used
X** protocols. If you find yourself playing with
X** this file too much, you may be making a mistake!
X*/
X
X
X
X
X/*
X** Header info table
X** Final (null) entry contains the flags used for any other field.
X**
X** Not all of these are actually handled specially by sendmail
X** at this time. They are included as placeholders, to let
X** you know that "someday" I intend to have sendmail do
X** something with them.
X*/
X
Xstruct hdrinfo HdrInfo[] =
X{
X /* originator fields, most to least significant */
X "resent-sender", H_FROM|H_RESENT,
X "resent-from", H_FROM|H_RESENT,
X "resent-reply-to", H_FROM|H_RESENT,
X "sender", H_FROM,
X "from", H_FROM,
X "reply-to", H_FROM,
X "full-name", H_ACHECK,
X "return-receipt-to", H_FROM,
X "errors-to", H_FROM,
X /* destination fields */
X "to", H_RCPT,
X "resent-to", H_RCPT|H_RESENT,
X "cc", H_RCPT,
X "resent-cc", H_RCPT|H_RESENT,
X "bcc", H_RCPT|H_ACHECK,
X "resent-bcc", H_RCPT|H_ACHECK|H_RESENT,
X /* message identification and control */
X "message-id", 0,
X "resent-message-id", H_RESENT,
X "message", H_EOH,
X "text", H_EOH,
X /* date fields */
X "date", 0,
X "resent-date", H_RESENT,
X /* trace fields */
X "received", H_TRACE|H_FORCE,
X "via", H_TRACE|H_FORCE,
X "mail-from", H_TRACE|H_FORCE,
X
X NULL, 0,
X};
X
X
X/*
X** ARPANET error message numbers.
X*/
X
Xchar Arpa_Info[] = "050"; /* arbitrary info */
Xchar Arpa_TSyserr[] = "451"; /* some (transient) system error */
Xchar Arpa_PSyserr[] = "554"; /* some (permanent) system error */
Xchar Arpa_Usrerr[] = "554"; /* some (fatal) user error */
X
X
X
X/*
X** Location of system files/databases/etc.
X*/
X
Xchar *ConfFile = "/usr/lib/sendmail.cf"; /* runtime configuration */
Xchar *FreezeFile = "/usr/lib/sendmail.fc"; /* frozen version of above */
X
X
X
X/*
X** Miscellaneous stuff.
X*/
X
Xint DtableSize = 50; /* max open files; reset in 4.2bsd */
X/*
X** SETDEFAULTS -- set default values
X**
X** Because of the way freezing is done, these must be initialized
X** using direct code.
X**
X** Parameters:
X** none.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Initializes a bunch of global variables to their
X** default values.
X*/
X
Xsetdefaults()
X{
X QueueLA = 8;
X QueueFactor = 10000;
X RefuseLA = 12;
X SpaceSub = ' ';
X WkRecipFact = 1000;
X WkClassFact = 1800;
X WkTimeFact = 9000;
X FileMode = 0644;
X DefUid = 1;
X DefGid = 1;
X}
X
X# ifdef V6
X/*
X** TTYNAME -- return name of terminal.
X**
X** Parameters:
X** fd -- file descriptor to check.
X**
X** Returns:
X** pointer to full path of tty.
X** NULL if no tty.
X**
X** Side Effects:
X** none.
X*/
X
Xchar *
Xttyname(fd)
X int fd;
X{
X register char tn;
X static char pathn[] = "/dev/ttyx";
X
X /* compute the pathname of the controlling tty */
X if ((tn = ttyn(fd)) == NULL)
X {
X errno = 0;
X return (NULL);
X }
X pathn[8] = tn;
X return (pathn);
X}
X/*
X** FDOPEN -- Open a stdio file given an open file descriptor.
X**
X** This is included here because it is standard in v7, but we
X** need it in v6.
X**
X** Algorithm:
X** Open /dev/null to create a descriptor.
X** Close that descriptor.
X** Copy the existing fd into the descriptor.
X**
X** Parameters:
X** fd -- the open file descriptor.
X** type -- "r", "w", or whatever.
X**
X** Returns:
X** The file descriptor it creates.
X**
X** Side Effects:
X** none
X**
X** Called By:
X** deliver
X**
X** Notes:
X** The mode of fd must match "type".
X*/
X
XFILE *
Xfdopen(fd, type)
X int fd;
X char *type;
X{
X register FILE *f;
X
X f = fopen("/dev/null", type);
X (void) close(fileno(f));
X fileno(f) = fd;
X return (f);
X}
X/*
X** INDEX -- Return pointer to character in string
X**
X** For V7 compatibility.
X**
X** Parameters:
X** s -- a string to scan.
X** c -- a character to look for.
X**
X** Returns:
X** If c is in s, returns the address of the first
X** instance of c in s.
X** NULL if c is not in s.
X**
X** Side Effects:
X** none.
X*/
X
Xchar *
Xindex(s, c)
X register char *s;
X register char c;
X{
X while (*s != '\0')
X {
X if (*s++ == c)
X return (--s);
X }
X return (NULL);
X}
X/*
X** UMASK -- fake the umask system call.
X**
X** Since V6 always acts like the umask is zero, we will just
X** assume the same thing.
X*/
X
X/*ARGSUSED*/
Xumask(nmask)
X{
X return (0);
X}
X
X
X/*
X** GETRUID -- get real user id.
X*/
X
Xgetruid()
X{
X return (getuid() & 0377);
X}
X
X
X/*
X** GETRGID -- get real group id.
X*/
X
Xgetrgid()
X{
X return (getgid() & 0377);
X}
X
X
X/*
X** GETEUID -- get effective user id.
X*/
X
Xgeteuid()
X{
X return ((getuid() >> 8) & 0377);
X}
X
X
X/*
X** GETEGID -- get effective group id.
X*/
X
Xgetegid()
X{
X return ((getgid() >> 8) & 0377);
X}
X
X# endif V6
X
X# ifndef V6
X
X/*
X** GETRUID -- get real user id (V7)
X*/
X
Xgetruid()
X{
X if (OpMode == MD_DAEMON)
X return (RealUid);
X else
X return (getuid());
X}
X
X
X/*
X** GETRGID -- get real group id (V7).
X*/
X
Xgetrgid()
X{
X if (OpMode == MD_DAEMON)
X return (RealGid);
X else
X return (getgid());
X}
X
X# endif V6
X/*
X** USERNAME -- return the user id of the logged in user.
X**
X** Parameters:
X** none.
X**
X** Returns:
X** The login name of the logged in user.
X**
X** Side Effects:
X** none.
X**
X** Notes:
X** The return value is statically allocated.
X*/
X
Xchar *
Xusername()
X{
X static char *myname = NULL;
X extern char *getlogin();
X register struct passwd *pw;
X extern struct passwd *getpwuid();
X
X /* cache the result */
X if (myname == NULL)
X {
X myname = getlogin();
X if (myname == NULL || myname[0] == '\0')
X {
X
X pw = getpwuid(getruid());
X if (pw != NULL)
X myname = pw->pw_name;
X }
X else
X {
X
X pw = getpwnam(myname);
X if(getuid() != pw->pw_uid)
X {
X pw = getpwuid(getuid());
X if (pw != NULL)
X myname = pw->pw_name;
X }
X }
X if (myname == NULL || myname[0] == '\0')
X {
X syserr("Who are you?");
X myname = "postmaster";
X }
X }
X
X return (myname);
X}
X/*
X** TTYPATH -- Get the path of the user's tty
X**
X** Returns the pathname of the user's tty. Returns NULL if
X** the user is not logged in or if s/he has write permission
X** denied.
X**
X** Parameters:
X** none
X**
X** Returns:
X** pathname of the user's tty.
X** NULL if not logged in or write permission denied.
X**
X** Side Effects:
X** none.
X**
X** WARNING:
X** Return value is in a local buffer.
X**
X** Called By:
X** savemail
X*/
X
X# include <sys/stat.h>
X
Xchar *
Xttypath()
X{
X struct stat stbuf;
X register char *pathn;
X extern char *ttyname();
X extern char *getlogin();
X
X /* compute the pathname of the controlling tty */
X if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
X (pathn = ttyname(0)) == NULL)
X {
X errno = 0;
X return (NULL);
X }
X
X /* see if we have write permission */
X if (stat(pathn, &stbuf) < 0 || !bitset(02, stbuf.st_mode))
X {
X errno = 0;
X return (NULL);
X }
X
X /* see if the user is logged in */
X if (getlogin() == NULL)
X return (NULL);
X
X /* looks good */
X return (pathn);
X}
X/*
X** CHECKCOMPAT -- check for From and To person compatible.
X**
X** This routine can be supplied on a per-installation basis
X** to determine whether a person is allowed to send a message.
X** This allows restriction of certain types of internet
X** forwarding or registration of users.
X**
X** If the hosts are found to be incompatible, an error
X** message should be given using "usrerr" and FALSE should
X** be returned.
X**
X** 'NoReturn' can be set to suppress the return-to-sender
X** function; this should be done on huge messages.
X**
X** Parameters:
X** to -- the person being sent to.
X**
X** Returns:
X** TRUE -- ok to send.
X** FALSE -- not ok.
X**
X** Side Effects:
X** none (unless you include the usrerr stuff)
X*/
X
Xbool
Xcheckcompat(to)
X register ADDRESS *to;
X{
X# ifdef lint
X if (to == NULL)
X to++;
X# endif lint
X# ifdef EXAMPLE_CODE
X /* this code is intended as an example only */
X register STAB *s;
X
X s = stab("arpa", ST_MAILER, ST_FIND);
X if (s != NULL && CurEnv->e_from.q_mailer != LocalMailer &&
X to->q_mailer == s->s_mailer)
X {
X usrerr("No ARPA mail through this machine: see your system administration");
X /* NoReturn = TRUE; to supress return copy */
X return (FALSE);
X }
X# endif EXAMPLE_CODE
X return (TRUE);
X}
X/*
X** HOLDSIGS -- arrange to hold all signals
X**
X** Parameters:
X** none.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Arranges that signals are held.
X*/
X
Xholdsigs()
X{
X}
X/*
X** RLSESIGS -- arrange to release all signals
X**
X** This undoes the effect of holdsigs.
X**
X** Parameters:
X** none.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Arranges that signals are released.
X*/
X
Xrlsesigs()
X{
X}
X/*
X** GETLA -- get the current load average
X**
X** This code stolen from la.c.
X**
X** Parameters:
X** none.
X**
X** Returns:
X** The current load average as an integer.
X**
X** Side Effects:
X** none.
X*/
X
X#ifdef VMUNIX
X
X#include <nlist.h>
X
Xstruct nlist Nl[] =
X{
X { "_avenrun" },
X#define X_AVENRUN 0
X { 0 },
X};
X
Xgetla()
X{
X static int kmem = -1;
X# ifdef sun
X long avenrun[3];
X# else
X double avenrun[3];
X# endif
X extern off_t lseek();
X
X if (kmem < 0)
X {
X kmem = open("/dev/kmem", 0, 0);
X if (kmem < 0)
X return (-1);
X (void) ioctl(kmem, (int) FIOCLEX, (char *) 0);
X nlist("/vmunix", Nl);
X if (Nl[0].n_type == 0)
X return (-1);
X }
X if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, 0) == -1 ||
X read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
X {
X /* thank you Ian */
X return (-1);
X }
X# ifdef sun
X return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
X# else
X return ((int) (avenrun[0] + 0.5));
X# endif
X}
X
X#else VMUNIX
X
Xgetla()
X{
X return (0);
X}
X
X#endif VMUNIX
X/*
X** SHOULDQUEUE -- should this message be queued or sent?
X**
X** Compares the message cost to the load average to decide.
X**
X** Parameters:
X** pri -- the priority of the message in question.
X**
X** Returns:
X** TRUE -- if this message should be queued up for the
X** time being.
X** FALSE -- if the load is low enough to send this message.
X**
X** Side Effects:
X** none.
X*/
X
Xbool
Xshouldqueue(pri)
X long pri;
X{
X int la;
X
X la = getla();
X if (la < QueueLA)
X return (FALSE);
X return (pri > (QueueFactor / (la - QueueLA + 1)));
X}
X/*
X** SETPROCTITLE -- set process title for ps
X**
X** Parameters:
X** fmt -- a printf style format string.
X** a, b, c -- possible parameters to fmt.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Clobbers argv of our main procedure so ps(1) will
X** display the title.
X*/
X
X/*VARARGS1*/
Xsetproctitle(fmt, a, b, c)
X char *fmt;
X{
X# ifdef SETPROCTITLE
X register char *p;
X register int i;
X extern char **Argv;
X extern char *LastArgv;
X char buf[MAXLINE];
X
X (void) sprintf(buf, fmt, a, b, c);
X
X /* make ps print "(sendmail)" */
X p = Argv[0];
X *p++ = '-';
X
X i = strlen(buf);
X if (i > LastArgv - p - 2)
X {
X i = LastArgv - p - 2;
X buf[i] = '\0';
X }
X (void) strcpy(p, buf);
X p += i;
X while (p < LastArgv)
X *p++ = ' ';
X# endif SETPROCTITLE
X}
X/*
X** REAPCHILD -- pick up the body of my child, lest it become a zombie
X**
X** Parameters:
X** none.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Picks up extant zombies.
X*/
X
X# ifdef VMUNIX
X# include <sys/wait.h>
X# endif VMUNIX
X
Xreapchild()
X{
X# ifdef WNOHANG
X union wait status;
X
X while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0)
X continue;
X# else WNOHANG
X auto int status;
X
X while (wait(&status) > 0)
X continue;
X# endif WNOHANG
X}
END_OF_FILE
if test 12327 -ne `wc -c <'src/conf.c'`; then
echo shar: \"'src/conf.c'\" unpacked with wrong size!
fi
# end of 'src/conf.c'
fi
if test -f 'src/envelope.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/envelope.c'\"
else
echo shar: Extracting \"'src/envelope.c'\" \(12701 characters\)
sed "s/^X//" >'src/envelope.c' <<'END_OF_FILE'
X/*
X** Sendmail
X** Copyright (c) 1983 Eric P. Allman
X** Berkeley, California
X**
X** Copyright (c) 1983 Regents of the University of California.
X** All rights reserved. The Berkeley software License Agreement
X** specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char SccsId[] = "@(#)envelope.c 5.12 (Berkeley) 12/17/85";
X#endif not lint
X
X#include <pwd.h>
X#include "sendmail.h"
X#ifdef UNIXPC
X#include <time.h>
X#else
X#include <sys/time.h>
X#endif
X#include <sys/stat.h>
X
X/*
X** NEWENVELOPE -- allocate a new envelope
X**
X** Supports inheritance.
X**
X** Parameters:
X** e -- the new envelope to fill in.
X**
X** Returns:
X** e.
X**
X** Side Effects:
X** none.
X*/
X
XENVELOPE *
Xnewenvelope(e)
X register ENVELOPE *e;
X{
X register ENVELOPE *parent;
X extern putheader(), putbody();
X extern ENVELOPE BlankEnvelope;
X
X parent = CurEnv;
X if (e == CurEnv)
X parent = e->e_parent;
X clearenvelope(e, TRUE);
X if (e == CurEnv)
X bcopy((char *) &NullAddress, (char *) &e->e_from, sizeof e->e_from);
X else
X bcopy((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from);
X e->e_parent = parent;
X e->e_ctime = curtime();
X e->e_msgpriority = parent->e_msgsize;
X e->e_puthdr = putheader;
X e->e_putbody = putbody;
X if (CurEnv->e_xfp != NULL)
X (void) fflush(CurEnv->e_xfp);
X
X return (e);
X}
X/*
X** DROPENVELOPE -- deallocate an envelope.
X**
X** Parameters:
X** e -- the envelope to deallocate.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** housekeeping necessary to dispose of an envelope.
X** Unlocks this queue file.
X*/
X
Xdropenvelope(e)
X register ENVELOPE *e;
X{
X bool queueit = FALSE;
X register ADDRESS *q;
X
X#ifdef DEBUG
X if (tTd(50, 1))
X {
X printf("dropenvelope %x id=", e);
X xputs(e->e_id);
X printf(" flags=%o\n", e->e_flags);
X }
X#endif DEBUG
X#ifdef LOG
X if (LogLevel > 10)
X syslog(LOG_DEBUG, "dropenvelope, id=%s, flags=%o, pid=%d",
X e->e_id == NULL ? "(none)" : e->e_id,
X e->e_flags, getpid());
X#endif LOG
X
X /* we must have an id to remove disk files */
X if (e->e_id == NULL)
X return;
X
X /*
X ** Extract state information from dregs of send list.
X */
X
X for (q = e->e_sendqueue; q != NULL; q = q->q_next)
X {
X if (bitset(QQUEUEUP, q->q_flags))
X queueit = TRUE;
X }
X
X /*
X ** Send back return receipts as requested.
X */
X
X if (e->e_receiptto != NULL && bitset(EF_SENDRECEIPT, e->e_flags))
X {
X auto ADDRESS *rlist = NULL;
X
X sendtolist(CurEnv->e_receiptto, (ADDRESS *) NULL, &rlist);
X (void) returntosender("Return receipt", rlist, FALSE);
X }
X
X /*
X ** Arrange to send error messages if there are fatal errors.
X */
X
X if (bitset(EF_FATALERRS|EF_TIMEOUT, e->e_flags) && ErrorMode != EM_QUIET)
X savemail(e);
X
X /*
X ** Instantiate or deinstantiate the queue.
X */
X
X if ((!queueit && !bitset(EF_KEEPQUEUE, e->e_flags)) ||
X bitset(EF_CLRQUEUE, e->e_flags))
X {
X if (e->e_df != NULL)
X xunlink(e->e_df);
X xunlink(queuename(e, 'q'));
X }
X else if (queueit || !bitset(EF_INQUEUE, e->e_flags))
X {
X#ifdef QUEUE
X queueup(e, FALSE, FALSE);
X#else QUEUE
X syserr("dropenvelope: queueup");
X#endif QUEUE
X }
X
X /* now unlock the job */
X closexscript(e);
X unlockqueue(e);
X
X /* make sure that this envelope is marked unused */
X e->e_id = e->e_df = NULL;
X if (e->e_dfp != NULL)
X (void) fclose(e->e_dfp);
X e->e_dfp = NULL;
X}
X/*
X** CLEARENVELOPE -- clear an envelope without unlocking
X**
X** This is normally used by a child process to get a clean
X** envelope without disturbing the parent.
X**
X** Parameters:
X** e -- the envelope to clear.
X** fullclear - if set, the current envelope is total
X** garbage and should be ignored; otherwise,
X** release any resources it may indicate.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Closes files associated with the envelope.
X** Marks the envelope as unallocated.
X*/
X
Xclearenvelope(e, fullclear)
X register ENVELOPE *e;
X bool fullclear;
X{
X register HDR *bh;
X register HDR **nhp;
X extern ENVELOPE BlankEnvelope;
X
X if (!fullclear)
X {
X /* clear out any file information */
X if (e->e_xfp != NULL)
X (void) fclose(e->e_xfp);
X if (e->e_dfp != NULL)
X (void) fclose(e->e_dfp);
X }
X
X /* now clear out the data */
X STRUCTCOPY(BlankEnvelope, *e);
X bh = BlankEnvelope.e_header;
X nhp = &e->e_header;
X while (bh != NULL)
X {
X *nhp = (HDR *) xalloc(sizeof *bh);
X bcopy((char *) bh, (char *) *nhp, sizeof *bh);
X bh = bh->h_link;
X nhp = &(*nhp)->h_link;
X }
X}
X/*
X** INITSYS -- initialize instantiation of system
X**
X** In Daemon mode, this is done in the child.
X**
X** Parameters:
X** none.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Initializes the system macros, some global variables,
X** etc. In particular, the current time in various
X** forms is set.
X*/
X
Xinitsys()
X{
X static char cbuf[5]; /* holds hop count */
X static char pbuf[10]; /* holds pid */
X#ifdef TTYNAME
X static char ybuf[10]; /* holds tty id */
X register char *p;
X#endif TTYNAME
X extern char *ttyname();
X extern char *macvalue();
X extern char Version[];
X
X /*
X ** Give this envelope a reality.
X ** I.e., an id, a transcript, and a creation time.
X */
X
X openxscript(CurEnv);
X CurEnv->e_ctime = curtime();
X
X /*
X ** Set OutChannel to something useful if stdout isn't it.
X ** This arranges that any extra stuff the mailer produces
X ** gets sent back to the user on error (because it is
X ** tucked away in the transcript).
X */
X
X if (OpMode == MD_DAEMON && QueueRun)
X OutChannel = CurEnv->e_xfp;
X
X /*
X ** Set up some basic system macros.
X */
X
X /* process id */
X (void) sprintf(pbuf, "%d", getpid());
X define('p', pbuf, CurEnv);
X
X /* hop count */
X (void) sprintf(cbuf, "%d", CurEnv->e_hopcount);
X define('c', cbuf, CurEnv);
X
X /* time as integer, unix time, arpa time */
X settime();
X
X#ifdef TTYNAME
X /* tty name */
X if (macvalue('y', CurEnv) == NULL)
X {
X p = ttyname(2);
X if (p != NULL)
X {
X if (rindex(p, '/') != NULL)
X p = rindex(p, '/') + 1;
X (void) strcpy(ybuf, p);
X define('y', ybuf, CurEnv);
X }
X }
X#endif TTYNAME
X}
X/*
X** SETTIME -- set the current time.
X**
X** Parameters:
X** none.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Sets the various time macros -- $a, $b, $d, $t.
X*/
X
Xsettime()
X{
X register char *p;
X auto time_t now;
X static char tbuf[20]; /* holds "current" time */
X static char dbuf[30]; /* holds ctime(tbuf) */
X register struct tm *tm;
X extern char *arpadate();
X extern struct tm *gmtime();
X extern char *macvalue();
X
X now = curtime();
X tm = gmtime(&now);
X (void) sprintf(tbuf, "%02d%02d%02d%02d%02d", tm->tm_year, tm->tm_mon+1,
X tm->tm_mday, tm->tm_hour, tm->tm_min);
X define('t', tbuf, CurEnv);
X (void) strcpy(dbuf, ctime(&now));
X *index(dbuf, '\n') = '\0';
X if (macvalue('d', CurEnv) == NULL)
X define('d', dbuf, CurEnv);
X p = newstr(arpadate(dbuf));
X if (macvalue('a', CurEnv) == NULL)
X define('a', p, CurEnv);
X define('b', p, CurEnv);
X}
X/*
X** OPENXSCRIPT -- Open transcript file
X**
X** Creates a transcript file for possible eventual mailing or
X** sending back.
X**
X** Parameters:
X** e -- the envelope to create the transcript in/for.
X**
X** Returns:
X** none
X**
X** Side Effects:
X** Creates the transcript file.
X*/
X
Xopenxscript(e)
X register ENVELOPE *e;
X{
X register char *p;
X
X# ifdef LOG
X if (LogLevel > 19)
X syslog(LOG_DEBUG, "%s: openx%s", e->e_id, e->e_xfp == NULL ? "" : " (no)");
X# endif LOG
X if (e->e_xfp != NULL)
X return;
X p = queuename(e, 'x');
X e->e_xfp = fopen(p, "w");
X if (e->e_xfp == NULL)
X syserr("Can't create %s", p);
X else
X (void) chmod(p, 0644);
X}
X/*
X** CLOSEXSCRIPT -- close the transcript file.
X**
X** Parameters:
X** e -- the envelope containing the transcript to close.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** none.
X*/
X
Xclosexscript(e)
X register ENVELOPE *e;
X{
X if (e->e_xfp == NULL)
X return;
X (void) fclose(e->e_xfp);
X e->e_xfp = NULL;
X}
X/*
X** SETSENDER -- set the person who this message is from
X**
X** Under certain circumstances allow the user to say who
X** s/he is (using -f or -r). These are:
X** 1. The user's uid is zero (root).
X** 2. The user's login name is in an approved list (typically
X** from a network server).
X** 3. The address the user is trying to claim has a
X** "!" character in it (since #2 doesn't do it for
X** us if we are dialing out for UUCP).
X** A better check to replace #3 would be if the
X** effective uid is "UUCP" -- this would require me
X** to rewrite getpwent to "grab" uucp as it went by,
X** make getname more nasty, do another passwd file
X** scan, or compile the UID of "UUCP" into the code,
X** all of which are reprehensible.
X**
X** Assuming all of these fail, we figure out something
X** ourselves.
X**
X** Parameters:
X** from -- the person we would like to believe this message
X** is from, as specified on the command line.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** sets sendmail's notion of who the from person is.
X*/
X
Xsetsender(from)
X char *from;
X{
X register char **pvp;
X char *realname = NULL;
X register struct passwd *pw;
X char buf[MAXNAME];
X char pvpbuf[PSBUFSIZE];
X extern struct passwd *getpwnam();
X extern char *macvalue();
X extern char **prescan();
X extern bool safefile();
X extern char *FullName;
X
X# ifdef DEBUG
X if (tTd(45, 1))
X printf("setsender(%s)\n", from == NULL ? "" : from);
X# endif DEBUG
X
X /*
X ** Figure out the real user executing us.
X ** Username can return errno != 0 on non-errors.
X */
X
X if (QueueRun || OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
X realname = from;
X if (realname == NULL || realname[0] == '\0')
X {
X extern char *username();
X
X realname = username();
X }
X
X /*
X ** Determine if this real person is allowed to alias themselves.
X */
X
X if (from != NULL)
X {
X extern bool trusteduser();
X
X if (!trusteduser(realname) &&
X# ifdef DEBUG
X (!tTd(1, 9) || getuid() != geteuid()) &&
X# endif DEBUG
X index(from, '!') == NULL && getuid() != 0)
X {
X /* network sends -r regardless (why why why?) */
X /* syserr("%s, you cannot use the -f flag", realname); */
X from = NULL;
X }
X }
X
X SuprErrs = TRUE;
X if (from == NULL || parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL)
X {
X /* log garbage addresses for traceback */
X if (from != NULL)
X {
X# ifdef LOG
X if (LogLevel >= 1)
X syslog(LOG_ERR, "Unparseable user %s wants to be %s",
X realname, from);
X# endif LOG
X }
X from = newstr(realname);
X if (parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL &&
X parseaddr("postmaster", &CurEnv->e_from, 1, '\0') == NULL)
X {
X syserr("setsender: can't even parse postmaster!");
X }
X }
X else
X FromFlag = TRUE;
X CurEnv->e_from.q_flags |= QDONTSEND;
X loweraddr(&CurEnv->e_from);
X SuprErrs = FALSE;
X
X if (CurEnv->e_from.q_mailer == LocalMailer &&
X (pw = getpwnam(CurEnv->e_from.q_user)) != NULL)
X {
X /*
X ** Process passwd file entry.
X */
X
X
X /* extract home directory */
X CurEnv->e_from.q_home = newstr(pw->pw_dir);
X define('z', CurEnv->e_from.q_home, CurEnv);
X
X /* extract user and group id */
X CurEnv->e_from.q_uid = pw->pw_uid;
X CurEnv->e_from.q_gid = pw->pw_gid;
X
X /* if the user has given fullname already, don't redefine */
X if (FullName == NULL)
X FullName = macvalue('x', CurEnv);
X if (FullName != NULL && FullName[0] == '\0')
X FullName = NULL;
X
X /* extract full name from passwd file */
X if (FullName == NULL && pw->pw_gecos != NULL &&
X strcmp(pw->pw_name, CurEnv->e_from.q_user) == 0)
X {
X buildfname(pw->pw_gecos, CurEnv->e_from.q_user, buf);
X if (buf[0] != '\0')
X FullName = newstr(buf);
X }
X if (FullName != NULL)
X define('x', FullName, CurEnv);
X }
X else
X {
X#ifndef V6
X if (CurEnv->e_from.q_home == NULL)
X CurEnv->e_from.q_home = getenv("HOME");
X#endif V6
X CurEnv->e_from.q_uid = getuid();
X CurEnv->e_from.q_gid = getgid();
X }
X
X if (CurEnv->e_from.q_uid != 0)
X {
X DefUid = CurEnv->e_from.q_uid;
X DefGid = CurEnv->e_from.q_gid;
X }
X
X /*
X ** Rewrite the from person to dispose of possible implicit
X ** links in the net.
X */
X
X pvp = prescan(from, '\0', pvpbuf);
X if (pvp == NULL)
X {
X syserr("cannot prescan from (%s)", from);
X finis();
X }
X rewrite(pvp, 3);
X rewrite(pvp, 1);
X rewrite(pvp, 4);
X cataddr(pvp, buf, sizeof buf);
X define('f', newstr(buf), CurEnv);
X
X /* save the domain spec if this mailer wants it */
X if (CurEnv->e_from.q_mailer != NULL &&
X bitnset(M_CANONICAL, CurEnv->e_from.q_mailer->m_flags))
X {
X extern char **copyplist();
X
X while (*pvp != NULL && strcmp(*pvp, "@") != 0)
X pvp++;
X if (*pvp != NULL)
X CurEnv->e_fromdomain = copyplist(pvp, TRUE);
X }
X}
X/*
X** TRUSTEDUSER -- tell us if this user is to be trusted.
X**
X** Parameters:
X** user -- the user to be checked.
X**
X** Returns:
X** TRUE if the user is in an approved list.
X** FALSE otherwise.
X**
X** Side Effects:
X** none.
X*/
X
Xbool
Xtrusteduser(user)
X char *user;
X{
X register char **ulist;
X extern char *TrustedUsers[];
X
X for (ulist = TrustedUsers; *ulist != NULL; ulist++)
X if (strcmp(*ulist, user) == 0)
X return (TRUE);
X return (FALSE);
X}
END_OF_FILE
if test 12701 -ne `wc -c <'src/envelope.c'`; then
echo shar: \"'src/envelope.c'\" unpacked with wrong size!
fi
# end of 'src/envelope.c'
fi
if test -f 'src/recipient.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/recipient.c'\"
else
echo shar: Extracting \"'src/recipient.c'\" \(12741 characters\)
sed "s/^X//" >'src/recipient.c' <<'END_OF_FILE'
X/*
X** Sendmail
X** Copyright (c) 1983 Eric P. Allman
X** Berkeley, California
X**
X** Copyright (c) 1983 Regents of the University of California.
X** All rights reserved. The Berkeley software License Agreement
X** specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char SccsId[] = "@(#)recipient.c 5.9 (Berkeley) 10/23/86";
X#endif not lint
X
X# include <pwd.h>
X# include "sendmail.h"
X# include <sys/stat.h>
X
X/*
X** SENDTOLIST -- Designate a send list.
X**
X** The parameter is a comma-separated list of people to send to.
X** This routine arranges to send to all of them.
X**
X** Parameters:
X** list -- the send list.
X** ctladdr -- the address template for the person to
X** send to -- effective uid/gid are important.
X** This is typically the alias that caused this
X** expansion.
X** sendq -- a pointer to the head of a queue to put
X** these people into.
X**
X** Returns:
X** none
X**
X** Side Effects:
X** none.
X*/
X
X# define MAXRCRSN 10
X
Xsendtolist(list, ctladdr, sendq)
X char *list;
X ADDRESS *ctladdr;
X ADDRESS **sendq;
X{
X register char *p;
X register ADDRESS *al; /* list of addresses to send to */
X bool firstone; /* set on first address sent */
X bool selfref; /* set if this list includes ctladdr */
X char delimiter; /* the address delimiter */
X
X# ifdef DEBUG
X if (tTd(25, 1))
X {
X printf("sendto: %s\n ctladdr=", list);
X printaddr(ctladdr, FALSE);
X }
X# endif DEBUG
X
X /* heuristic to determine old versus new style addresses */
X if (ctladdr == NULL &&
X (index(list, ',') != NULL || index(list, ';') != NULL ||
X index(list, '<') != NULL || index(list, '(') != NULL))
X CurEnv->e_flags &= ~EF_OLDSTYLE;
X delimiter = ' ';
X if (!bitset(EF_OLDSTYLE, CurEnv->e_flags) || ctladdr != NULL)
X delimiter = ',';
X
X firstone = TRUE;
X selfref = FALSE;
X al = NULL;
X
X for (p = list; *p != '\0'; )
X {
X register ADDRESS *a;
X extern char *DelimChar; /* defined in prescan */
X
X /* parse the address */
X while (isspace(*p) || *p == ',')
X p++;
X a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter);
X p = DelimChar;
X if (a == NULL)
X continue;
X a->q_next = al;
X a->q_alias = ctladdr;
X
X /* see if this should be marked as a primary address */
X if (ctladdr == NULL ||
X (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags)))
X a->q_flags |= QPRIMARY;
X
X /* put on send queue or suppress self-reference */
X if (ctladdr != NULL && sameaddr(ctladdr, a))
X selfref = TRUE;
X else
X al = a;
X firstone = FALSE;
X }
X
X /* if this alias doesn't include itself, delete ctladdr */
X if (!selfref && ctladdr != NULL)
X ctladdr->q_flags |= QDONTSEND;
X
X /* arrange to send to everyone on the local send list */
X while (al != NULL)
X {
X register ADDRESS *a = al;
X extern ADDRESS *recipient();
X
X al = a->q_next;
X a = recipient(a, sendq);
X
X /* arrange to inherit full name */
X if (a->q_fullname == NULL && ctladdr != NULL)
X a->q_fullname = ctladdr->q_fullname;
X }
X
X CurEnv->e_to = NULL;
X}
X/*
X** RECIPIENT -- Designate a message recipient
X**
X** Saves the named person for future mailing.
X**
X** Parameters:
X** a -- the (preparsed) address header for the recipient.
X** sendq -- a pointer to the head of a queue to put the
X** recipient in. Duplicate supression is done
X** in this queue.
X**
X** Returns:
X** The actual address in the queue. This will be "a" if
X** the address is not a duplicate, else the original address.
X**
X** Side Effects:
X** none.
X*/
X
XADDRESS *
Xrecipient(a, sendq)
X register ADDRESS *a;
X register ADDRESS **sendq;
X{
X register ADDRESS *q;
X ADDRESS **pq;
X register struct mailer *m;
X register char *p;
X bool quoted = FALSE; /* set if the addr has a quote bit */
X char buf[MAXNAME]; /* unquoted image of the user name */
X extern ADDRESS *getctladdr();
X extern bool safefile();
X
X CurEnv->e_to = a->q_paddr;
X m = a->q_mailer;
X errno = 0;
X# ifdef DEBUG
X if (tTd(26, 1))
X {
X printf("\nrecipient: ");
X printaddr(a, FALSE);
X }
X# endif DEBUG
X
X /* break aliasing loops */
X if (AliasLevel > MAXRCRSN)
X {
X usrerr("aliasing/forwarding loop broken");
X return (a);
X }
X
X /*
X ** Finish setting up address structure.
X */
X
X /* set the queue timeout */
X a->q_timeout = TimeOut;
X
X /* map user & host to lower case if requested on non-aliases */
X if (a->q_alias == NULL)
X loweraddr(a);
X
X /* get unquoted user for file, program or user.name check */
X (void) strcpy(buf, a->q_user);
X for (p = buf; *p != '\0' && !quoted; p++)
X {
X if (!isascii(*p) && (*p & 0377) != (SpaceSub & 0377))
X quoted = TRUE;
X }
X stripquotes(buf, TRUE);
X
X /* do sickly crude mapping for program mailing, etc. */
X if (m == LocalMailer && buf[0] == '|')
X {
X a->q_mailer = m = ProgMailer;
X a->q_user++;
X if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
X {
X a->q_flags |= QDONTSEND|QBADADDR;
X usrerr("Cannot mail directly to programs");
X }
X }
X
X /*
X ** Look up this person in the recipient list.
X ** If they are there already, return, otherwise continue.
X ** If the list is empty, just add it. Notice the cute
X ** hack to make from addresses suppress things correctly:
X ** the QDONTSEND bit will be set in the send list.
X ** [Please note: the emphasis is on "hack."]
X */
X
X for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
X {
X if (!ForceMail && sameaddr(q, a))
X {
X# ifdef DEBUG
X if (tTd(26, 1))
X {
X printf("%s in sendq: ", a->q_paddr);
X printaddr(q, FALSE);
X }
X# endif DEBUG
X if (!bitset(QDONTSEND, a->q_flags))
X message(Arpa_Info, "duplicate suppressed");
X if (!bitset(QPRIMARY, q->q_flags))
X q->q_flags |= a->q_flags;
X return (q);
X }
X }
X
X /* add address on list */
X *pq = a;
X a->q_next = NULL;
X CurEnv->e_nrcpts++;
X
X /*
X ** Alias the name and handle :include: specs.
X */
X
X if (m == LocalMailer && !bitset(QDONTSEND, a->q_flags))
X {
X if (strncmp(a->q_user, ":include:", 9) == 0)
X {
X a->q_flags |= QDONTSEND;
X if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
X {
X a->q_flags |= QBADADDR;
X usrerr("Cannot mail directly to :include:s");
X }
X else
X {
X message(Arpa_Info, "including file %s", &a->q_user[9]);
X include(&a->q_user[9], " sending", a, sendq);
X }
X }
X else
X alias(a, sendq);
X }
X
X /*
X ** If the user is local and still being sent, verify that
X ** the address is good. If it is, try to forward.
X ** If the address is already good, we have a forwarding
X ** loop. This can be broken by just sending directly to
X ** the user (which is probably correct anyway).
X */
X
X if (!bitset(QDONTSEND, a->q_flags) && m == LocalMailer)
X {
X struct stat stb;
X extern bool writable();
X
X /* see if this is to a file */
X if (buf[0] == '/')
X {
X p = rindex(buf, '/');
X /* check if writable or creatable */
X if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
X {
X a->q_flags |= QDONTSEND|QBADADDR;
X usrerr("Cannot mail directly to files");
X }
X else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) :
X (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC)))
X {
X a->q_flags |= QBADADDR;
X giveresponse(EX_CANTCREAT, m, CurEnv);
X }
X }
X else
X {
X register struct passwd *pw;
X extern struct passwd *finduser();
X
X /* warning -- finduser may trash buf */
X pw = finduser(buf);
X if (pw == NULL)
X {
X a->q_flags |= QBADADDR;
X giveresponse(EX_NOUSER, m, CurEnv);
X }
X else
X {
X char nbuf[MAXNAME];
X
X if (strcmp(a->q_user, pw->pw_name) != 0)
X {
X a->q_user = newstr(pw->pw_name);
X (void) strcpy(buf, pw->pw_name);
X }
X a->q_home = newstr(pw->pw_dir);
X a->q_uid = pw->pw_uid;
X a->q_gid = pw->pw_gid;
X a->q_flags |= QGOODUID;
X buildfname(pw->pw_gecos, pw->pw_name, nbuf);
X if (nbuf[0] != '\0')
X a->q_fullname = newstr(nbuf);
X if (!quoted)
X forward(a, sendq);
X }
X }
X }
X return (a);
X}
X/*
X** FINDUSER -- find the password entry for a user.
X**
X** This looks a lot like getpwnam, except that it may want to
X** do some fancier pattern matching in /etc/passwd.
X**
X** This routine contains most of the time of many sendmail runs.
X** It deserves to be optimized.
X**
X** Parameters:
X** name -- the name to match against.
X**
X** Returns:
X** A pointer to a pw struct.
X** NULL if name is unknown or ambiguous.
X**
X** Side Effects:
X** may modify name.
X*/
X
Xstruct passwd *
Xfinduser(name)
X char *name;
X{
X register struct passwd *pw;
X register char *p;
X extern struct passwd *getpwent();
X extern struct passwd *getpwnam();
X
X /* map upper => lower case */
X for (p = name; *p != '\0'; p++)
X {
X if (isascii(*p) && isupper(*p))
X *p = tolower(*p);
X }
X
X /* look up this login name using fast path */
X if ((pw = getpwnam(name)) != NULL)
X return (pw);
X
X /* search for a matching full name instead */
X for (p = name; *p != '\0'; p++)
X {
X if (*p == (SpaceSub & 0177) || *p == '_')
X *p = ' ';
X }
X (void) setpwent();
X while ((pw = getpwent()) != NULL)
X {
X char buf[MAXNAME];
X extern bool sameword();
X
X buildfname(pw->pw_gecos, pw->pw_name, buf);
X if (index(buf, ' ') != NULL && sameword(buf, name))
X {
X message(Arpa_Info, "sending to login name %s", pw->pw_name);
X return (pw);
X }
X }
X return (NULL);
X}
X/*
X** WRITABLE -- predicate returning if the file is writable.
X**
X** This routine must duplicate the algorithm in sys/fio.c.
X** Unfortunately, we cannot use the access call since we
X** won't necessarily be the real uid when we try to
X** actually open the file.
X**
X** Notice that ANY file with ANY execute bit is automatically
X** not writable. This is also enforced by mailfile.
X**
X** Parameters:
X** s -- pointer to a stat struct for the file.
X**
X** Returns:
X** TRUE -- if we will be able to write this file.
X** FALSE -- if we cannot write this file.
X**
X** Side Effects:
X** none.
X*/
X
Xbool
Xwritable(s)
X register struct stat *s;
X{
X int euid, egid;
X int bits;
X
X if (bitset(0111, s->st_mode))
X return (FALSE);
X euid = getruid();
X egid = getrgid();
X if (geteuid() == 0)
X {
X if (bitset(S_ISUID, s->st_mode))
X euid = s->st_uid;
X if (bitset(S_ISGID, s->st_mode))
X egid = s->st_gid;
X }
X
X if (euid == 0)
X return (TRUE);
X bits = S_IWRITE;
X if (euid != s->st_uid)
X {
X bits >>= 3;
X if (egid != s->st_gid)
X bits >>= 3;
X }
X return ((s->st_mode & bits) != 0);
X}
X/*
X** INCLUDE -- handle :include: specification.
X**
X** Parameters:
X** fname -- filename to include.
X** msg -- message to print in verbose mode.
X** ctladdr -- address template to use to fill in these
X** addresses -- effective user/group id are
X** the important things.
X** sendq -- a pointer to the head of the send queue
X** to put these addresses in.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** reads the :include: file and sends to everyone
X** listed in that file.
X*/
X
Xinclude(fname, msg, ctladdr, sendq)
X char *fname;
X char *msg;
X ADDRESS *ctladdr;
X ADDRESS **sendq;
X{
X char buf[MAXLINE];
X register FILE *fp;
X char *oldto = CurEnv->e_to;
X char *oldfilename = FileName;
X int oldlinenumber = LineNumber;
X
X fp = fopen(fname, "r");
X if (fp == NULL)
X {
X usrerr("Cannot open %s", fname);
X return;
X }
X if (getctladdr(ctladdr) == NULL)
X {
X struct stat st;
X
X if (fstat(fileno(fp), &st) < 0)
X syserr("Cannot fstat %s!", fname);
X ctladdr->q_uid = st.st_uid;
X ctladdr->q_gid = st.st_gid;
X ctladdr->q_flags |= QGOODUID;
X }
X
X /* read the file -- each line is a comma-separated list. */
X FileName = fname;
X LineNumber = 0;
X while (fgets(buf, sizeof buf, fp) != NULL)
X {
X register char *p = index(buf, '\n');
X
X if (p != NULL)
X *p = '\0';
X if (buf[0] == '\0')
X continue;
X CurEnv->e_to = oldto;
X message(Arpa_Info, "%s to %s", msg, buf);
X AliasLevel++;
X sendtolist(buf, ctladdr, sendq);
X AliasLevel--;
X }
X
X (void) fclose(fp);
X FileName = oldfilename;
X LineNumber = oldlinenumber;
X}
X/*
X** SENDTOARGV -- send to an argument vector.
X**
X** Parameters:
X** argv -- argument vector to send to.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** puts all addresses on the argument vector onto the
X** send queue.
X*/
X
Xsendtoargv(argv)
X register char **argv;
X{
X register char *p;
X extern bool sameword();
X
X while ((p = *argv++) != NULL)
X {
X if (argv[0] != NULL && argv[1] != NULL && sameword(argv[0], "at"))
X {
X char nbuf[MAXNAME];
X
X if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf)
X usrerr("address overflow");
X else
X {
X (void) strcpy(nbuf, p);
X (void) strcat(nbuf, "@");
X (void) strcat(nbuf, argv[1]);
X p = newstr(nbuf);
X argv += 2;
X }
X }
X sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
X }
X}
X/*
X** GETCTLADDR -- get controlling address from an address header.
X**
X** If none, get one corresponding to the effective userid.
X**
X** Parameters:
X** a -- the address to find the controller of.
X**
X** Returns:
X** the controlling address.
X**
X** Side Effects:
X** none.
X*/
X
XADDRESS *
Xgetctladdr(a)
X register ADDRESS *a;
X{
X while (a != NULL && !bitset(QGOODUID, a->q_flags))
X a = a->q_alias;
X return (a);
X}
END_OF_FILE
if test 12741 -ne `wc -c <'src/recipient.c'`; then
echo shar: \"'src/recipient.c'\" unpacked with wrong size!
fi
# end of 'src/recipient.c'
fi
if test -f 'src/savemail.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/savemail.c'\"
else
echo shar: Extracting \"'src/savemail.c'\" \(11670 characters\)
sed "s/^X//" >'src/savemail.c' <<'END_OF_FILE'
X/*
X** Sendmail
X** Copyright (c) 1983 Eric P. Allman
X** Berkeley, California
X**
X** Copyright (c) 1983 Regents of the University of California.
X** All rights reserved. The Berkeley software License Agreement
X** specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char SccsId[] = "@(#)savemail.c 5.7 (Berkeley) 12/7/85";
X#endif not lint
X
X# include <pwd.h>
X# include "sendmail.h"
X
X/*
X** SAVEMAIL -- Save mail on error
X**
X** If mailing back errors, mail it back to the originator
X** together with an error message; otherwise, just put it in
X** dead.letter in the user's home directory (if he exists on
X** this machine).
X**
X** Parameters:
X** e -- the envelope containing the message in error.
X**
X** Returns:
X** none
X**
X** Side Effects:
X** Saves the letter, by writing or mailing it back to the
X** sender, or by putting it in dead.letter in her home
X** directory.
X*/
X
X/* defines for state machine */
X# define ESM_REPORT 0 /* report to sender's terminal */
X# define ESM_MAIL 1 /* mail back to sender */
X# define ESM_QUIET 2 /* messages have already been returned */
X# define ESM_DEADLETTER 3 /* save in ~/dead.letter */
X# define ESM_POSTMASTER 4 /* return to postmaster */
X# define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */
X# define ESM_PANIC 6 /* leave the locked queue/transcript files */
X# define ESM_DONE 7 /* the message is successfully delivered */
X
X
Xsavemail(e)
X register ENVELOPE *e;
X{
X register struct passwd *pw;
X register FILE *fp;
X int state;
X auto ADDRESS *q;
X char buf[MAXLINE+1];
X extern struct passwd *getpwnam();
X register char *p;
X extern char *ttypath();
X typedef int (*fnptr)();
X
X# ifdef DEBUG
X if (tTd(6, 1))
X printf("\nsavemail, ErrorMode = %c\n", ErrorMode);
X# endif DEBUG
X
X if (bitset(EF_RESPONSE, e->e_flags))
X return;
X if (e->e_class < 0)
X {
X message(Arpa_Info, "Dumping junk mail");
X return;
X }
X ForceMail = TRUE;
X e->e_flags &= ~EF_FATALERRS;
X
X /*
X ** In the unhappy event we don't know who to return the mail
X ** to, make someone up.
X */
X
X if (e->e_from.q_paddr == NULL)
X {
X if (parseaddr("root", &e->e_from, 0, '\0') == NULL)
X {
X syserr("Cannot parse root!");
X ExitStat = EX_SOFTWARE;
X finis();
X }
X }
X e->e_to = NULL;
X
X /*
X ** Basic state machine.
X **
X ** This machine runs through the following states:
X **
X ** ESM_QUIET Errors have already been printed iff the
X ** sender is local.
X ** ESM_REPORT Report directly to the sender's terminal.
X ** ESM_MAIL Mail response to the sender.
X ** ESM_DEADLETTER Save response in ~/dead.letter.
X ** ESM_POSTMASTER Mail response to the postmaster.
X ** ESM_PANIC Save response anywhere possible.
X */
X
X /* determine starting state */
X switch (ErrorMode)
X {
X case EM_WRITE:
X state = ESM_REPORT;
X break;
X
X case EM_BERKNET:
X /* mail back, but return o.k. exit status */
X ExitStat = EX_OK;
X
X /* fall through.... */
X
X case EM_MAIL:
X state = ESM_MAIL;
X break;
X
X case EM_PRINT:
X case '\0':
X state = ESM_QUIET;
X break;
X
X case EM_QUIET:
X /* no need to return anything at all */
X return;
X
X default:
X syserr("savemail: ErrorMode x%x\n");
X state = ESM_MAIL;
X break;
X }
X
X while (state != ESM_DONE)
X {
X# ifdef DEBUG
X if (tTd(6, 5))
X printf(" state %d\n", state);
X# endif DEBUG
X
X switch (state)
X {
X case ESM_QUIET:
X if (e->e_from.q_mailer == LocalMailer)
X state = ESM_DEADLETTER;
X else
X state = ESM_MAIL;
X break;
X
X case ESM_REPORT:
X
X /*
X ** If the user is still logged in on the same terminal,
X ** then write the error messages back to hir (sic).
X */
X
X p = ttypath();
X if (p == NULL || freopen(p, "w", stdout) == NULL)
X {
X state = ESM_MAIL;
X break;
X }
X
X expand("\001n", buf, &buf[sizeof buf - 1], e);
X printf("\r\nMessage from %s...\r\n", buf);
X printf("Errors occurred while sending mail.\r\n");
X if (e->e_xfp != NULL)
X {
X (void) fflush(e->e_xfp);
X fp = fopen(queuename(e, 'x'), "r");
X }
X else
X fp = NULL;
X if (fp == NULL)
X {
X syserr("Cannot open %s", queuename(e, 'x'));
X printf("Transcript of session is unavailable.\r\n");
X }
X else
X {
X printf("Transcript follows:\r\n");
X while (fgets(buf, sizeof buf, fp) != NULL &&
X !ferror(stdout))
X fputs(buf, stdout);
X (void) fclose(fp);
X }
X printf("Original message will be saved in dead.letter.\r\n");
X if (ferror(stdout))
X (void) syserr("savemail: stdout: write err");
X state = ESM_DEADLETTER;
X break;
X
X case ESM_MAIL:
X case ESM_POSTMASTER:
X /*
X ** If mailing back, do it.
X ** Throw away all further output. Don't alias,
X ** since this could cause loops, e.g., if joe
X ** mails to joe at x, and for some reason the network
X ** for @x is down, then the response gets sent to
X ** joe at x, which gives a response, etc. Also force
X ** the mail to be delivered even if a version of
X ** it has already been sent to the sender.
X */
X
X if (state == ESM_MAIL)
X {
X if (e->e_errorqueue == NULL)
X sendtolist(e->e_from.q_paddr,
X (ADDRESS *) NULL,
X &e->e_errorqueue);
X
X /* deliver a cc: to the postmaster if desired */
X if (PostMasterCopy != NULL)
X sendtolist(PostMasterCopy,
X (ADDRESS *) NULL,
X &e->e_errorqueue);
X q = e->e_errorqueue;
X }
X else
X {
X if (parseaddr("postmaster", q, 0, '\0') == NULL)
X {
X syserr("cannot parse postmaster!");
X ExitStat = EX_SOFTWARE;
X state = ESM_USRTMP;
X break;
X }
X }
X if (returntosender(e->e_message != NULL ? e->e_message :
X "Unable to deliver mail",
X q, TRUE) == 0)
X {
X state = ESM_DONE;
X break;
X }
X
X state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP;
X break;
X
X case ESM_DEADLETTER:
X /*
X ** Save the message in dead.letter.
X ** If we weren't mailing back, and the user is
X ** local, we should save the message in
X ** ~/dead.letter so that the poor person doesn't
X ** have to type it over again -- and we all know
X ** what poor typists UNIX users are.
X */
X
X p = NULL;
X if (e->e_from.q_mailer == LocalMailer)
X {
X if (e->e_from.q_home != NULL)
X p = e->e_from.q_home;
X else if ((pw = getpwnam(e->e_from.q_user)) != NULL)
X p = pw->pw_dir;
X }
X if (p == NULL)
X {
X syserr("Can't return mail to %s", e->e_from.q_paddr);
X state = ESM_MAIL;
X break;
X }
X if (e->e_dfp != NULL)
X {
X auto ADDRESS *q;
X bool oldverb = Verbose;
X
X /* we have a home directory; open dead.letter */
X define('z', p, e);
X expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e);
X Verbose = TRUE;
X message(Arpa_Info, "Saving message in %s", buf);
X Verbose = oldverb;
X e->e_to = buf;
X q = NULL;
X sendtolist(buf, (ADDRESS *) NULL, &q);
X if (deliver(e, q) == 0)
X state = ESM_DONE;
X else
X state = ESM_MAIL;
X }
X else
X {
X /* no data file -- try mailing back */
X state = ESM_MAIL;
X }
X break;
X
X case ESM_USRTMP:
X /*
X ** Log the mail in /usr/tmp/dead.letter.
X */
X
X fp = dfopen("/usr/tmp/dead.letter", "a");
X if (fp == NULL)
X {
X state = ESM_PANIC;
X break;
X }
X
X putfromline(fp, ProgMailer);
X (*e->e_puthdr)(fp, ProgMailer, e);
X putline("\n", fp, ProgMailer);
X (*e->e_putbody)(fp, ProgMailer, e);
X putline("\n", fp, ProgMailer);
X (void) fflush(fp);
X state = ferror(fp) ? ESM_PANIC : ESM_DONE;
X (void) fclose(fp);
X break;
X
X default:
X syserr("savemail: unknown state %d", state);
X
X /* fall through ... */
X
X case ESM_PANIC:
X syserr("savemail: HELP!!!!");
X# ifdef LOG
X if (LogLevel >= 1)
X syslog(LOG_ALERT, "savemail: HELP!!!!");
X# endif LOG
X
X /* leave the locked queue & transcript files around */
X exit(EX_SOFTWARE);
X }
X }
X}
X/*
X** RETURNTOSENDER -- return a message to the sender with an error.
X**
X** Parameters:
X** msg -- the explanatory message.
X** returnq -- the queue of people to send the message to.
X** sendbody -- if TRUE, also send back the body of the
X** message; otherwise just send the header.
X**
X** Returns:
X** zero -- if everything went ok.
X** else -- some error.
X**
X** Side Effects:
X** Returns the current message to the sender via
X** mail.
X*/
X
Xstatic bool SendBody;
X
X#define MAXRETURNS 6 /* max depth of returning messages */
X
Xreturntosender(msg, returnq, sendbody)
X char *msg;
X ADDRESS *returnq;
X bool sendbody;
X{
X char buf[MAXNAME];
X extern putheader(), errbody();
X register ENVELOPE *ee;
X extern ENVELOPE *newenvelope();
X ENVELOPE errenvelope;
X static int returndepth;
X register ADDRESS *q;
X
X# ifdef DEBUG
X if (tTd(6, 1))
X {
X printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n",
X msg, returndepth, CurEnv);
X printf("\treturnq=");
X printaddr(returnq, TRUE);
X }
X# endif DEBUG
X
X if (++returndepth >= MAXRETURNS)
X {
X if (returndepth != MAXRETURNS)
X syserr("returntosender: infinite recursion on %s", returnq->q_paddr);
X /* don't "unrecurse" and fake a clean exit */
X /* returndepth--; */
X return (0);
X }
X
X SendBody = sendbody;
X define('g', "\001f", CurEnv);
X ee = newenvelope(&errenvelope);
X define('a', "\001b", ee);
X ee->e_puthdr = putheader;
X ee->e_putbody = errbody;
X ee->e_flags |= EF_RESPONSE;
X ee->e_sendqueue = returnq;
X openxscript(ee);
X for (q = returnq; q != NULL; q = q->q_next)
X {
X if (q->q_alias == NULL)
X addheader("to", q->q_paddr, ee);
X }
X
X (void) sprintf(buf, "Returned mail: %s", msg);
X addheader("subject", buf, ee);
X
X /* fake up an address header for the from person */
X expand("\001n", buf, &buf[sizeof buf - 1], CurEnv);
X if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL)
X {
X syserr("Can't parse myself!");
X ExitStat = EX_SOFTWARE;
X returndepth--;
X return (-1);
X }
X loweraddr(&ee->e_from);
X
X /* push state into submessage */
X CurEnv = ee;
X define('f', "\001n", ee);
X define('x', "Mail Delivery Subsystem", ee);
X eatheader(ee);
X
X /* actually deliver the error message */
X sendall(ee, SM_DEFAULT);
X
X /* restore state */
X dropenvelope(ee);
X CurEnv = CurEnv->e_parent;
X returndepth--;
X
X /* should check for delivery errors here */
X return (0);
X}
X/*
X** ERRBODY -- output the body of an error message.
X**
X** Typically this is a copy of the transcript plus a copy of the
X** original offending message.
X**
X** Parameters:
X** fp -- the output file.
X** m -- the mailer to output to.
X** e -- the envelope we are working in.
X**
X** Returns:
X** none
X**
X** Side Effects:
X** Outputs the body of an error message.
X*/
X
Xerrbody(fp, m, e)
X register FILE *fp;
X register struct mailer *m;
X register ENVELOPE *e;
X{
X register FILE *xfile;
X char buf[MAXLINE];
X char *p;
X
X /*
X ** Output transcript of errors
X */
X
X (void) fflush(stdout);
X p = queuename(e->e_parent, 'x');
X if ((xfile = fopen(p, "r")) == NULL)
X {
X syserr("Cannot open %s", p);
X fprintf(fp, " ----- Transcript of session is unavailable -----\n");
X }
X else
X {
X fprintf(fp, " ----- Transcript of session follows -----\n");
X if (e->e_xfp != NULL)
X (void) fflush(e->e_xfp);
X while (fgets(buf, sizeof buf, xfile) != NULL)
X putline(buf, fp, m);
X (void) fclose(xfile);
X }
X errno = 0;
X
X /*
X ** Output text of original message
X */
X
X if (NoReturn)
X fprintf(fp, "\n ----- Return message suppressed -----\n\n");
X else if (e->e_parent->e_dfp != NULL)
X {
X if (SendBody)
X {
X putline("\n", fp, m);
X putline(" ----- Unsent message follows -----\n", fp, m);
X (void) fflush(fp);
X putheader(fp, m, e->e_parent);
X putline("\n", fp, m);
X putbody(fp, m, e->e_parent);
X }
X else
X {
X putline("\n", fp, m);
X putline(" ----- Message header follows -----\n", fp, m);
X (void) fflush(fp);
X putheader(fp, m, e->e_parent);
X }
X }
X else
X {
X putline("\n", fp, m);
X putline(" ----- No message was collected -----\n", fp, m);
X putline("\n", fp, m);
X }
X
X /*
X ** Cleanup and exit
X */
X
X if (errno != 0)
X syserr("errbody: I/O error");
X}
END_OF_FILE
if test 11670 -ne `wc -c <'src/savemail.c'`; then
echo shar: \"'src/savemail.c'\" unpacked with wrong size!
fi
# end of 'src/savemail.c'
fi
echo shar: End of archive 3 \(of 8\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 8 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
David H. Brierley
Home: dave at galaxia.Newport.RI.US {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb at rayssd.ray.com {sun,decuac,gatech,necntc,ukma}!rayssd!dhb
More information about the Unix-pc.sources
mailing list