Sendmail source for the 3B1/7300 (part 2/8)
David H. Brierley
dave at galaxia.Newport.RI.US
Sat Feb 25 12:27:50 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 2 (of 8)."
# Contents: src/alias.c src/collect.c src/domain.c src/err.c
# src/macro.c src/usersmtp.c
# Wrapped by dave at galaxia on Fri Feb 24 20:23:41 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/alias.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/alias.c'\"
else
echo shar: Extracting \"'src/alias.c'\" \(11559 characters\)
sed "s/^X//" >'src/alias.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# include "sendmail.h"
X# include <pwd.h>
X# include <sys/stat.h>
X# include <signal.h>
X# include <errno.h>
X# ifdef FLOCK
X# include <sys/file.h>
X# endif FLOCK
X
X#ifndef lint
X# ifdef DBM
Xstatic char SccsId[] = "@(#)alias.c 5.13 (Berkeley) 4/17/86 (with DBM)";
X# else DBM
Xstatic char SccsId[] = "@(#)alias.c 5.13 (Berkeley) 4/17/86 (without DBM)";
X# endif DBM
X#endif not lint
X
X
X/*
X** ALIAS -- Compute aliases.
X**
X** Scans the alias file for an alias for the given address.
X** If found, it arranges to deliver to the alias list instead.
X** Uses libdbm database if -DDBM.
X**
X** Parameters:
X** a -- address to alias.
X** sendq -- a pointer to the head of the send queue
X** to put the aliases in.
X**
X** Returns:
X** none
X**
X** Side Effects:
X** Aliases found are expanded.
X**
X** Notes:
X** If NoAlias (the "-n" flag) is set, no aliasing is
X** done.
X**
X** Deficiencies:
X** It should complain about names that are aliased to
X** nothing.
X*/
X
X
X#ifdef DBM
Xtypedef struct
X{
X char *dptr;
X int dsize;
X} DATUM;
Xextern DATUM fetch();
X#endif DBM
X
Xalias(a, sendq)
X register ADDRESS *a;
X ADDRESS **sendq;
X{
X register char *p;
X extern char *aliaslookup();
X
X# ifdef DEBUG
X if (tTd(27, 1))
X printf("alias(%s)\n", a->q_paddr);
X# endif
X
X /* don't realias already aliased names */
X if (bitset(QDONTSEND, a->q_flags))
X return;
X
X CurEnv->e_to = a->q_paddr;
X
X /*
X ** Look up this name
X */
X
X if (NoAlias)
X p = NULL;
X else
X p = aliaslookup(a->q_user);
X if (p == NULL)
X return;
X
X /*
X ** Match on Alias.
X ** Deliver to the target list.
X */
X
X# ifdef DEBUG
X if (tTd(27, 1))
X printf("%s (%s, %s) aliased to %s\n",
X a->q_paddr, a->q_host, a->q_user, p);
X# endif
X message(Arpa_Info, "aliased to %s", p);
X AliasLevel++;
X sendtolist(p, a, sendq);
X AliasLevel--;
X}
X/*
X** ALIASLOOKUP -- look up a name in the alias file.
X**
X** Parameters:
X** name -- the name to look up.
X**
X** Returns:
X** the value of name.
X** NULL if unknown.
X**
X** Side Effects:
X** none.
X**
X** Warnings:
X** The return value will be trashed across calls.
X*/
X
Xchar *
Xaliaslookup(name)
X char *name;
X{
X# ifdef DBM
X DATUM rhs, lhs;
X
X /* create a key for fetch */
X lhs.dptr = name;
X lhs.dsize = strlen(name) + 1;
X rhs = fetch(lhs);
X return (rhs.dptr);
X# else DBM
X register STAB *s;
X
X s = stab(name, ST_ALIAS, ST_FIND);
X if (s == NULL)
X return (NULL);
X return (s->s_alias);
X# endif DBM
X}
X/*
X** INITALIASES -- initialize for aliasing
X**
X** Very different depending on whether we are running DBM or not.
X**
X** Parameters:
X** aliasfile -- location of aliases.
X** init -- if set and if DBM, initialize the DBM files.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** initializes aliases:
X** if DBM: opens the database.
X** if ~DBM: reads the aliases into the symbol table.
X*/
X
X# define DBMMODE 0666
X
Xinitaliases(aliasfile, init)
X char *aliasfile;
X bool init;
X{
X#ifdef DBM
X int atcnt;
X time_t modtime;
X bool automatic = FALSE;
X char buf[MAXNAME];
X#endif DBM
X struct stat stb;
X static bool initialized = FALSE;
X
X if (initialized)
X return;
X initialized = TRUE;
X
X if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
X {
X if (aliasfile != NULL && init)
X syserr("Cannot open %s", aliasfile);
X NoAlias = TRUE;
X errno = 0;
X return;
X }
X
X# ifdef DBM
X /*
X ** Check to see that the alias file is complete.
X ** If not, we will assume that someone died, and it is up
X ** to us to rebuild it.
X */
X
X if (!init)
X dbminit(aliasfile);
X atcnt = SafeAlias * 2;
X if (atcnt > 0)
X {
X while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
X {
X /*
X ** Reinitialize alias file in case the new
X ** one is mv'ed in instead of cp'ed in.
X **
X ** Only works with new DBM -- old one will
X ** just consume file descriptors forever.
X ** If you have a dbmclose() it can be
X ** added before the sleep(30).
X */
X
X sleep(30);
X# ifdef NDBM
X dbminit(aliasfile);
X# endif NDBM
X }
X }
X else
X atcnt = 1;
X
X /*
X ** See if the DBM version of the file is out of date with
X ** the text version. If so, go into 'init' mode automatically.
X ** This only happens if our effective userid owns the DBM
X ** version or if the mode of the database is 666 -- this
X ** is an attempt to avoid protection problems. Note the
X ** unpalatable hack to see if the stat succeeded.
X */
X
X modtime = stb.st_mtime;
X (void) strcpy(buf, aliasfile);
X (void) strcat(buf, ".pag");
X stb.st_ino = 0;
X if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
X {
X errno = 0;
X if (AutoRebuild && stb.st_ino != 0 &&
X ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
X {
X init = TRUE;
X automatic = TRUE;
X message(Arpa_Info, "rebuilding alias database");
X#ifdef LOG
X if (LogLevel >= 7)
X syslog(LOG_INFO, "rebuilding alias database");
X#endif LOG
X }
X else
X {
X#ifdef LOG
X if (LogLevel >= 7)
X syslog(LOG_INFO, "alias database out of date");
X#endif LOG
X message(Arpa_Info, "Warning: alias database out of date");
X }
X }
X
X
X /*
X ** If necessary, load the DBM file.
X ** If running without DBM, load the symbol table.
X */
X
X if (init)
X {
X#ifdef LOG
X if (LogLevel >= 6)
X {
X extern char *username();
X
X syslog(LOG_NOTICE, "alias database %srebuilt by %s",
X automatic ? "auto" : "", username());
X }
X#endif LOG
X readaliases(aliasfile, TRUE);
X }
X# else DBM
X readaliases(aliasfile, init);
X# endif DBM
X}
X/*
X** READALIASES -- read and process the alias file.
X**
X** This routine implements the part of initaliases that occurs
X** when we are not going to use the DBM stuff.
X**
X** Parameters:
X** aliasfile -- the pathname of the alias file master.
X** init -- if set, initialize the DBM stuff.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Reads aliasfile into the symbol table.
X** Optionally, builds the .dir & .pag files.
X*/
X
Xstatic
Xreadaliases(aliasfile, init)
X char *aliasfile;
X bool init;
X{
X register char *p;
X char *rhs;
X bool skipping;
X int naliases, bytes, longest;
X FILE *af;
X int (*oldsigint)();
X ADDRESS al, bl;
X register STAB *s;
X char line[BUFSIZ];
X
X if ((af = fopen(aliasfile, "r")) == NULL)
X {
X# ifdef DEBUG
X if (tTd(27, 1))
X printf("Can't open %s\n", aliasfile);
X# endif
X errno = 0;
X NoAlias++;
X return;
X }
X
X# ifdef DBM
X# ifdef FLOCK
X /* see if someone else is rebuilding the alias file already */
X if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
X {
X /* yes, they are -- wait until done and then return */
X message(Arpa_Info, "Alias file is already being rebuilt");
X if (OpMode != MD_INITALIAS)
X {
X /* wait for other rebuild to complete */
X (void) flock(fileno(af), LOCK_EX);
X }
X (void) fclose(af);
X errno = 0;
X return;
X }
X# endif FLOCK
X
X /*
X ** If initializing, create the new DBM files.
X */
X
X if (init)
X {
X oldsigint = signal(SIGINT, SIG_IGN);
X (void) strcpy(line, aliasfile);
X (void) strcat(line, ".dir");
X if (close(creat(line, DBMMODE)) < 0)
X {
X syserr("cannot make %s", line);
X (void) signal(SIGINT, oldsigint);
X return;
X }
X (void) strcpy(line, aliasfile);
X (void) strcat(line, ".pag");
X if (close(creat(line, DBMMODE)) < 0)
X {
X syserr("cannot make %s", line);
X (void) signal(SIGINT, oldsigint);
X return;
X }
X dbminit(aliasfile);
X }
X# endif DBM
X
X /*
X ** Read and interpret lines
X */
X
X FileName = aliasfile;
X LineNumber = 0;
X naliases = bytes = longest = 0;
X skipping = FALSE;
X while (fgets(line, sizeof (line), af) != NULL)
X {
X int lhssize, rhssize;
X
X LineNumber++;
X p = index(line, '\n');
X if (p != NULL)
X *p = '\0';
X switch (line[0])
X {
X case '#':
X case '\0':
X skipping = FALSE;
X continue;
X
X case ' ':
X case '\t':
X if (!skipping)
X syserr("Non-continuation line starts with space");
X skipping = TRUE;
X continue;
X }
X skipping = FALSE;
X
X /*
X ** Process the LHS
X ** Find the final colon, and parse the address.
X ** It should resolve to a local name -- this will
X ** be checked later (we want to optionally do
X ** parsing of the RHS first to maximize error
X ** detection).
X */
X
X for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
X continue;
X if (*p++ != ':')
X {
X syserr("missing colon");
X continue;
X }
X if (parseaddr(line, &al, 1, ':') == NULL)
X {
X syserr("illegal alias name");
X continue;
X }
X loweraddr(&al);
X
X /*
X ** Process the RHS.
X ** 'al' is the internal form of the LHS address.
X ** 'p' points to the text of the RHS.
X */
X
X rhs = p;
X for (;;)
X {
X register char c;
X
X if (init && CheckAliases)
X {
X /* do parsing & compression of addresses */
X while (*p != '\0')
X {
X extern char *DelimChar;
X
X while (isspace(*p) || *p == ',')
X p++;
X if (*p == '\0')
X break;
X if (parseaddr(p, &bl, -1, ',') == NULL)
X usrerr("%s... bad address", p);
X p = DelimChar;
X }
X }
X else
X {
X p = &p[strlen(p)];
X if (p[-1] == '\n')
X *--p = '\0';
X }
X
X /* see if there should be a continuation line */
X c = fgetc(af);
X if (!feof(af))
X (void) ungetc(c, af);
X if (c != ' ' && c != '\t')
X break;
X
X /* read continuation line */
X if (fgets(p, sizeof line - (p - line), af) == NULL)
X break;
X LineNumber++;
X }
X if (al.q_mailer != LocalMailer)
X {
X syserr("cannot alias non-local names");
X continue;
X }
X
X /*
X ** Insert alias into symbol table or DBM file
X */
X
X lhssize = strlen(al.q_user) + 1;
X rhssize = strlen(rhs) + 1;
X
X# ifdef DBM
X if (init)
X {
X DATUM key, content;
X
X key.dsize = lhssize;
X key.dptr = al.q_user;
X content.dsize = rhssize;
X content.dptr = rhs;
X store(key, content);
X }
X else
X# endif DBM
X {
X s = stab(al.q_user, ST_ALIAS, ST_ENTER);
X s->s_alias = newstr(rhs);
X }
X
X /* statistics */
X naliases++;
X bytes += lhssize + rhssize;
X if (rhssize > longest)
X longest = rhssize;
X }
X
X# ifdef DBM
X if (init)
X {
X /* add the distinquished alias "@" */
X DATUM key;
X
X key.dsize = 2;
X key.dptr = "@";
X store(key, key);
X
X /* restore the old signal */
X (void) signal(SIGINT, oldsigint);
X }
X# endif DBM
X
X /* closing the alias file drops the lock */
X (void) fclose(af);
X CurEnv->e_to = NULL;
X FileName = NULL;
X message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
X naliases, longest, bytes);
X# ifdef LOG
X if (LogLevel >= 8)
X syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
X naliases, longest, bytes);
X# endif LOG
X}
X/*
X** FORWARD -- Try to forward mail
X**
X** This is similar but not identical to aliasing.
X**
X** Parameters:
X** user -- the name of the user who's mail we would like
X** to forward to. It must have been verified --
X** i.e., the q_home field must have been filled
X** in.
X** sendq -- a pointer to the head of the send queue to
X** put this user's aliases in.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** New names are added to send queues.
X*/
X
Xforward(user, sendq)
X ADDRESS *user;
X ADDRESS **sendq;
X{
X char buf[60];
X extern bool safefile();
X
X# ifdef DEBUG
X if (tTd(27, 1))
X printf("forward(%s)\n", user->q_paddr);
X# endif DEBUG
X
X if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
X return;
X# ifdef DEBUG
X if (user->q_home == NULL)
X syserr("forward: no home");
X# endif DEBUG
X
X /* good address -- look for .forward file in home */
X define('z', user->q_home, CurEnv);
X expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
X if (!safefile(buf, user->q_uid, S_IREAD))
X return;
X
X /* we do have an address to forward to -- do it */
X include(buf, "forwarding", user, sendq);
X}
END_OF_FILE
if test 11559 -ne `wc -c <'src/alias.c'`; then
echo shar: \"'src/alias.c'\" unpacked with wrong size!
fi
# end of 'src/alias.c'
fi
if test -f 'src/collect.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/collect.c'\"
else
echo shar: Extracting \"'src/collect.c'\" \(6760 characters\)
sed "s/^X//" >'src/collect.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[] = "@(#)collect.c 5.2 (Berkeley) 6/8/85";
X#endif not lint
X
X# include <errno.h>
X# include "sendmail.h"
X
X/*
X** COLLECT -- read & parse message header & make temp file.
X**
X** Creates a temporary file name and copies the standard
X** input to that file. Leading UNIX-style "From" lines are
X** stripped off (after important information is extracted).
X**
X** Parameters:
X** sayok -- if set, give an ARPANET style message
X** to say we are ready to collect input.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Temp file is created and filled.
X** The from person may be set.
X*/
X
Xcollect(sayok)
X bool sayok;
X{
X register FILE *tf;
X char buf[MAXFIELD+2];
X register char *p;
X extern char *hvalue();
X
X /*
X ** Create the temp file name and create the file.
X */
X
X CurEnv->e_df = newstr(queuename(CurEnv, 'd'));
X if ((tf = dfopen(CurEnv->e_df, "w")) == NULL)
X {
X syserr("Cannot create %s", CurEnv->e_df);
X NoReturn = TRUE;
X finis();
X }
X (void) chmod(CurEnv->e_df, FileMode);
X
X /*
X ** Tell ARPANET to go ahead.
X */
X
X if (sayok)
X message("354", "Enter mail, end with \".\" on a line by itself");
X
X /*
X ** Try to read a UNIX-style From line
X */
X
X (void) sfgets(buf, sizeof buf, InChannel);
X fixcrlf(buf, FALSE);
X# ifndef NOTUNIX
X if (!SaveFrom && strncmp(buf, "From ", 5) == 0)
X {
X eatfrom(buf);
X (void) sfgets(buf, sizeof buf, InChannel);
X fixcrlf(buf, FALSE);
X }
X# endif NOTUNIX
X
X /*
X ** Copy InChannel to temp file & do message editing.
X ** To keep certain mailers from getting confused,
X ** and to keep the output clean, lines that look
X ** like UNIX "From" lines are deleted in the header.
X */
X
X do
X {
X int c;
X extern bool isheader();
X
X /* drop out on error */
X if (ferror(InChannel))
X break;
X
X /* if the line is too long, throw the rest away */
X if (index(buf, '\n') == NULL)
X {
X while ((c = getc(InChannel)) != '\n' && c != EOF)
X continue;
X /* give an error? */
X }
X
X fixcrlf(buf, TRUE);
X
X /* see if the header is over */
X if (!isheader(buf))
X break;
X
X /* get the rest of this field */
X while ((c = getc(InChannel)) == ' ' || c == '\t')
X {
X p = &buf[strlen(buf)];
X *p++ = '\n';
X *p++ = c;
X if (sfgets(p, MAXFIELD - (p - buf), InChannel) == NULL)
X break;
X fixcrlf(p, TRUE);
X }
X if (!feof(InChannel) && !ferror(InChannel))
X (void) ungetc(c, InChannel);
X
X CurEnv->e_msgsize += strlen(buf);
X
X /*
X ** Snarf header away.
X */
X
X if (bitset(H_EOH, chompheader(buf, FALSE)))
X break;
X } while (sfgets(buf, MAXFIELD, InChannel) != NULL);
X
X# ifdef DEBUG
X if (tTd(30, 1))
X printf("EOH\n");
X# endif DEBUG
X
X /* throw away a blank line */
X if (buf[0] == '\0')
X (void) sfgets(buf, MAXFIELD, InChannel);
X
X /*
X ** Collect the body of the message.
X */
X
X do
X {
X register char *bp = buf;
X
X fixcrlf(buf, TRUE);
X
X /* check for end-of-message */
X if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
X break;
X
X /* check for transparent dot */
X if (OpMode == MD_SMTP && !IgnrDot && bp[0] == '.' && bp[1] == '.')
X bp++;
X
X /*
X ** Figure message length, output the line to the temp
X ** file, and insert a newline if missing.
X */
X
X CurEnv->e_msgsize += strlen(bp) + 1;
X fputs(bp, tf);
X fputs("\n", tf);
X if (ferror(tf))
X tferror(tf);
X } while (sfgets(buf, MAXFIELD, InChannel) != NULL);
X if (fflush(tf) != 0)
X tferror(tf);
X (void) fclose(tf);
X
X /* An EOF when running SMTP is an error */
X if ((feof(InChannel) || ferror(InChannel)) && OpMode == MD_SMTP)
X {
X syserr("collect: unexpected close, from=%s", CurEnv->e_from.q_paddr);
X
X /* don't return an error indication */
X CurEnv->e_to = NULL;
X CurEnv->e_flags &= ~EF_FATALERRS;
X
X /* and don't try to deliver the partial message either */
X finis();
X }
X
X /*
X ** Find out some information from the headers.
X ** Examples are who is the from person & the date.
X */
X
X eatheader(CurEnv);
X
X /*
X ** Add an Apparently-To: line if we have no recipient lines.
X */
X
X if (hvalue("to") == NULL && hvalue("cc") == NULL &&
X hvalue("bcc") == NULL && hvalue("apparently-to") == NULL)
X {
X register ADDRESS *q;
X
X /* create an Apparently-To: field */
X /* that or reject the message.... */
X for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
X {
X if (q->q_alias != NULL)
X continue;
X# ifdef DEBUG
X if (tTd(30, 3))
X printf("Adding Apparently-To: %s\n", q->q_paddr);
X# endif DEBUG
X addheader("apparently-to", q->q_paddr, CurEnv);
X }
X }
X
X if ((CurEnv->e_dfp = fopen(CurEnv->e_df, "r")) == NULL)
X syserr("Cannot reopen %s", CurEnv->e_df);
X}
X/*
X** TFERROR -- signal error on writing the temporary file.
X**
X** Parameters:
X** tf -- the file pointer for the temporary file.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Gives an error message.
X** Arranges for following output to go elsewhere.
X*/
X
Xtferror(tf)
X FILE *tf;
X{
X if (errno == ENOSPC)
X {
X (void) freopen(CurEnv->e_df, "w", tf);
X fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf);
X usrerr("452 Out of disk space for temp file");
X }
X else
X syserr("collect: Cannot write %s", CurEnv->e_df);
X (void) freopen("/dev/null", "w", tf);
X}
X/*
X** EATFROM -- chew up a UNIX style from line and process
X**
X** This does indeed make some assumptions about the format
X** of UNIX messages.
X**
X** Parameters:
X** fm -- the from line.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** extracts what information it can from the header,
X** such as the date.
X*/
X
X# ifndef NOTUNIX
X
Xchar *DowList[] =
X{
X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
X};
X
Xchar *MonthList[] =
X{
X "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X NULL
X};
X
Xeatfrom(fm)
X char *fm;
X{
X register char *p;
X register char **dt;
X
X# ifdef DEBUG
X if (tTd(30, 2))
X printf("eatfrom(%s)\n", fm);
X# endif DEBUG
X
X /* find the date part */
X p = fm;
X while (*p != '\0')
X {
X /* skip a word */
X while (*p != '\0' && *p != ' ')
X p++;
X while (*p == ' ')
X p++;
X if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':')
X continue;
X
X /* we have a possible date */
X for (dt = DowList; *dt != NULL; dt++)
X if (strncmp(*dt, p, 3) == 0)
X break;
X if (*dt == NULL)
X continue;
X
X for (dt = MonthList; *dt != NULL; dt++)
X if (strncmp(*dt, &p[4], 3) == 0)
X break;
X if (*dt != NULL)
X break;
X }
X
X if (*p != NULL)
X {
X char *q;
X extern char *arpadate();
X
X /* we have found a date */
X q = xalloc(25);
X (void) strncpy(q, p, 25);
X q[24] = '\0';
X define('d', q, CurEnv);
X q = arpadate(q);
X define('a', newstr(q), CurEnv);
X }
X}
X
X# endif NOTUNIX
END_OF_FILE
if test 6760 -ne `wc -c <'src/collect.c'`; then
echo shar: \"'src/collect.c'\" unpacked with wrong size!
fi
# end of 'src/collect.c'
fi
if test -f 'src/domain.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/domain.c'\"
else
echo shar: Extracting \"'src/domain.c'\" \(6431 characters\)
sed "s/^X//" >'src/domain.c' <<'END_OF_FILE'
X/*
X** Sendmail
X** Copyright (c) 1986 Eric P. Allman
X** Berkeley, California
X**
X** Copyright (c) 1986 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#include "sendmail.h"
X
X#ifndef MXDOMAIN
X#ifndef lint
Xstatic char SccsId[] = "@(#)domain.c 5.7 (Berkeley) 12/12/87 (no MXDOMAIN)";
X#endif not lint
X#else MXDOMAIN
X
X#ifndef lint
Xstatic char SccsId[] = "@(#)domain.c 5.7 (Berkeley) 12/12/87";
X#endif not lint
X
X# include <sys/param.h>
X# include <arpa/nameser.h>
X# include <resolv.h>
X# include <netdb.h>
X
Xtypedef union {
X HEADER qb1;
X char qb2[PACKETSZ];
X} querybuf;
X
Xstatic char hostbuf[BUFSIZ];
Xint h_errno;
Xextern u_short getshort();
X
Xgetmxrr(host, mxhosts, maxmx, localhost)
X char *host, **mxhosts;
X int maxmx;
X char *localhost;
X{
X
X HEADER *hp;
X char *eom, *bp, *cp;
X querybuf buf, answer;
X int n, n1, i, j, nmx, ancount, qdcount, buflen;
X int seenlocal;
X u_short prefer[BUFSIZ];
X u_short pref, localpref, type, class;
X
X n = res_mkquery(QUERY, host, C_IN, T_MX, (char *)NULL, 0, NULL,
X (char *)&buf, sizeof(buf));
X if (n < 0) {
X#ifdef DEBUG
X if (tTd(8, 1) || _res.options & RES_DEBUG)
X printf("res_mkquery failed\n");
X#endif
X h_errno = NO_RECOVERY;
X return(-2);
X }
X n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer));
X if (n < 0) {
X#ifdef DEBUG
X if (tTd(8, 1) || _res.options & RES_DEBUG)
X printf("res_send failed\n");
X#endif
X h_errno = TRY_AGAIN;
X return (-1);
X }
X eom = (char *)&answer + n;
X /*
X * find first satisfactory answer
X */
X hp = (HEADER *) &answer;
X ancount = ntohs(hp->ancount);
X qdcount = ntohs(hp->qdcount);
X if (hp->rcode != NOERROR || ancount == 0) {
X#ifdef DEBUG
X if (tTd(8, 1) || _res.options & RES_DEBUG)
X printf("rcode = %d, ancount=%d\n", hp->rcode, ancount);
X#endif
X switch (hp->rcode) {
X case NXDOMAIN:
X /* Check if it's an authoritive answer */
X if (hp->aa) {
X h_errno = HOST_NOT_FOUND;
X return(-3);
X } else {
X h_errno = TRY_AGAIN;
X return(-1);
X }
X case SERVFAIL:
X h_errno = TRY_AGAIN;
X return(-1);
X#ifdef OLDJEEVES
X /*
X * Jeeves (TOPS-20 server) still does not
X * support MX records. For the time being,
X * we must accept FORMERRs as the same as
X * NOERROR.
X */
X case FORMERR:
X#endif OLDJEEVES
X case NOERROR:
X (void) strcpy(hostbuf, host);
X mxhosts[0] = hostbuf;
X return(1);
X#ifndef OLDJEEVES
X case FORMERR:
X#endif OLDJEEVES
X case NOTIMP:
X case REFUSED:
X h_errno = NO_RECOVERY;
X return(-2);
X }
X return (-1);
X }
X bp = hostbuf;
X nmx = 0;
X seenlocal = 0;
X buflen = sizeof(hostbuf);
X cp = (char *)&answer + sizeof(HEADER);
X if (qdcount) {
X cp += dn_skipname(cp, eom) + QFIXEDSZ;
X while (--qdcount > 0)
X cp += dn_skipname(cp, eom) + QFIXEDSZ;
X }
X while (--ancount >= 0 && cp < eom && nmx < maxmx) {
X if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0)
X break;
X cp += n;
X type = getshort(cp);
X cp += sizeof(u_short);
X /*
X class = getshort(cp);
X */
X cp += sizeof(u_short) + sizeof(u_long);
X n = getshort(cp);
X cp += sizeof(u_short);
X if (type != T_MX) {
X#ifdef DEBUG
X if (tTd(8, 1) || _res.options & RES_DEBUG)
X printf("unexpected answer type %d, size %d\n",
X type, n);
X#endif
X cp += n;
X continue;
X }
X pref = getshort(cp);
X cp += sizeof(u_short);
X if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0)
X break;
X cp += n;
X if (sameword(bp, localhost))
X {
X seenlocal = 1;
X localpref = pref;
X continue;
X }
X prefer[nmx] = pref;
X mxhosts[nmx++] = bp;
X n1 = strlen(bp)+1;
X bp += n1;
X buflen -= n1;
X }
X if (nmx == 0) {
X (void) strcpy(hostbuf, host);
X mxhosts[0] = hostbuf;
X return(1);
X }
X /* sort the records */
X for (i = 0; i < nmx; i++) {
X for (j = i + 1; j < nmx; j++) {
X if (prefer[i] > prefer[j]) {
X int temp;
X char *temp1;
X
X temp = prefer[i];
X prefer[i] = prefer[j];
X prefer[j] = temp;
X temp1 = mxhosts[i];
X mxhosts[i] = mxhosts[j];
X mxhosts[j] = temp1;
X }
X }
X if (seenlocal && (prefer[i] >= localpref))
X {
X nmx = i;
X /*
X * We are the first MX, might as well try delivering
X * since nobody is supposed to have more info.
X */
X if (nmx == 0)
X {
X (void) strcpy(hostbuf, host);
X mxhosts[0] = hostbuf;
X return(1);
X }
X break;
X }
X }
X return(nmx);
X}
X
X
Xgetcanonname(host, hbsize)
X char *host;
X int hbsize;
X{
X
X HEADER *hp;
X char *eom, *cp;
X querybuf buf, answer;
X int n, ancount, qdcount;
X u_short type;
X char nbuf[BUFSIZ];
X int first;
X
X n = res_mkquery(QUERY, host, C_IN, T_ANY, (char *)NULL, 0, NULL,
X (char *)&buf, sizeof(buf));
X if (n < 0) {
X#ifdef DEBUG
X if (tTd(8, 1) || _res.options & RES_DEBUG)
X printf("res_mkquery failed\n");
X#endif
X h_errno = NO_RECOVERY;
X return;
X }
X n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer));
X if (n < 0) {
X#ifdef DEBUG
X if (tTd(8, 1) || _res.options & RES_DEBUG)
X printf("res_send failed\n");
X#endif
X h_errno = TRY_AGAIN;
X return;
X }
X eom = (char *)&answer + n;
X /*
X * find first satisfactory answer
X */
X hp = (HEADER *) &answer;
X ancount = ntohs(hp->ancount);
X qdcount = ntohs(hp->qdcount);
X /*
X * We don't care about errors here, only if we got an answer
X */
X if (ancount == 0) {
X#ifdef DEBUG
X if (tTd(8, 1) || _res.options & RES_DEBUG)
X printf("rcode = %d, ancount=%d\n", hp->rcode, ancount);
X#endif
X return;
X }
X cp = (char *)&answer + sizeof(HEADER);
X if (qdcount) {
X cp += dn_skipname(cp, eom) + QFIXEDSZ;
X while (--qdcount > 0)
X cp += dn_skipname(cp, eom) + QFIXEDSZ;
X }
X first = 1;
X while (--ancount >= 0 && cp < eom) {
X if ((n = dn_expand((char *)&answer, eom, cp, nbuf,
X sizeof(nbuf))) < 0)
X break;
X if (first) {
X (void)strncpy(host, nbuf, hbsize);
X host[hbsize - 1] = '\0';
X first = 0;
X }
X cp += n;
X type = getshort(cp);
X cp += sizeof(u_short);
X cp += sizeof(u_short) + sizeof(u_long);
X n = getshort(cp);
X cp += sizeof(u_short);
X if (type == T_CNAME) {
X /*
X * Assume that only one cname will be found. More
X * than one is undefined.
X */
X if ((n = dn_expand((char *)&answer, eom, cp, nbuf,
X sizeof(nbuf))) < 0)
X break;
X (void)strncpy(host, nbuf, hbsize);
X host[hbsize - 1] = '\0';
X getcanonname(host, hbsize);
X break;
X }
X cp += n;
X }
X return;
X}
X
Xu_short
Xgetshort(msgp)
X char *msgp;
X{
X register u_char *p = (u_char *) msgp;
X register int u;
X
X u = *p++ << 8;
X return ((u_short)(u | *p));
X}
X
X#endif MXDOMAIN
END_OF_FILE
if test 6431 -ne `wc -c <'src/domain.c'`; then
echo shar: \"'src/domain.c'\" unpacked with wrong size!
fi
# end of 'src/domain.c'
fi
if test -f 'src/err.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/err.c'\"
else
echo shar: Extracting \"'src/err.c'\" \(7229 characters\)
sed "s/^X//" >'src/err.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[] = "@(#)err.c 5.7 (Berkeley) 11/22/85";
X#endif not lint
X
X# include "sendmail.h"
X# include <errno.h>
X
X#ifdef SMTP
X# include <netdb.h>
X#endif
X
X/*
X** SYSERR -- Print error message.
X**
X** Prints an error message via printf to the diagnostic
X** output. If LOG is defined, it logs it also.
X**
X** Parameters:
X** f -- the format string
X** a, b, c, d, e -- parameters
X**
X** Returns:
X** none
X** Through TopFrame if QuickAbort is set.
X**
X** Side Effects:
X** increments Errors.
X** sets ExitStat.
X*/
X
X# ifdef lint
Xint sys_nerr;
Xchar *sys_errlist[];
X# endif lint
Xchar MsgBuf[BUFSIZ*2]; /* text of most recent message */
X
X/*VARARGS1*/
Xsyserr(fmt, a, b, c, d, e)
X char *fmt;
X{
X register char *p;
X int olderrno = errno;
X extern char Arpa_PSyserr[];
X extern char Arpa_TSyserr[];
X
X /* format and output the error message */
X if (olderrno == 0)
X p = Arpa_PSyserr;
X else
X p = Arpa_TSyserr;
X fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, a, b, c, d, e);
X puterrmsg(MsgBuf);
X
X /* determine exit status if not already set */
X if (ExitStat == EX_OK)
X {
X if (olderrno == 0)
X ExitStat = EX_SOFTWARE;
X else
X ExitStat = EX_OSERR;
X }
X
X# ifdef LOG
X if (LogLevel > 0)
X syslog(LOG_CRIT, "%s: SYSERR: %s",
X CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
X &MsgBuf[4]);
X# endif LOG
X errno = 0;
X if (QuickAbort)
X longjmp(TopFrame, 2);
X}
X/*
X** USRERR -- Signal user error.
X**
X** This is much like syserr except it is for user errors.
X**
X** Parameters:
X** fmt, a, b, c, d -- printf strings
X**
X** Returns:
X** none
X** Through TopFrame if QuickAbort is set.
X**
X** Side Effects:
X** increments Errors.
X*/
X
X/*VARARGS1*/
Xusrerr(fmt, a, b, c, d, e)
X char *fmt;
X{
X extern char SuprErrs;
X extern char Arpa_Usrerr[];
X extern int errno;
X
X if (SuprErrs)
X return;
X
X fmtmsg(MsgBuf, CurEnv->e_to, Arpa_Usrerr, errno, fmt, a, b, c, d, e);
X puterrmsg(MsgBuf);
X
X if (QuickAbort)
X longjmp(TopFrame, 1);
X}
X/*
X** MESSAGE -- print message (not necessarily an error)
X**
X** Parameters:
X** num -- the default ARPANET error number (in ascii)
X** msg -- the message (printf fmt) -- if it begins
X** with a digit, this number overrides num.
X** a, b, c, d, e -- printf arguments
X**
X** Returns:
X** none
X**
X** Side Effects:
X** none.
X*/
X
X/*VARARGS2*/
Xmessage(num, msg, a, b, c, d, e)
X register char *num;
X register char *msg;
X{
X errno = 0;
X fmtmsg(MsgBuf, CurEnv->e_to, num, 0, msg, a, b, c, d, e);
X putmsg(MsgBuf, FALSE);
X}
X/*
X** NMESSAGE -- print message (not necessarily an error)
X**
X** Just like "message" except it never puts the to... tag on.
X**
X** Parameters:
X** num -- the default ARPANET error number (in ascii)
X** msg -- the message (printf fmt) -- if it begins
X** with three digits, this number overrides num.
X** a, b, c, d, e -- printf arguments
X**
X** Returns:
X** none
X**
X** Side Effects:
X** none.
X*/
X
X/*VARARGS2*/
Xnmessage(num, msg, a, b, c, d, e)
X register char *num;
X register char *msg;
X{
X errno = 0;
X fmtmsg(MsgBuf, (char *) NULL, num, 0, msg, a, b, c, d, e);
X putmsg(MsgBuf, FALSE);
X}
X/*
X** PUTMSG -- output error message to transcript and channel
X**
X** Parameters:
X** msg -- message to output (in SMTP format).
X** holdmsg -- if TRUE, don't output a copy of the message to
X** our output channel.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Outputs msg to the transcript.
X** If appropriate, outputs it to the channel.
X** Deletes SMTP reply code number as appropriate.
X*/
X
Xputmsg(msg, holdmsg)
X char *msg;
X bool holdmsg;
X{
X /* output to transcript if serious */
X if (CurEnv->e_xfp != NULL && (msg[0] == '4' || msg[0] == '5'))
X fprintf(CurEnv->e_xfp, "%s\n", msg);
X
X /* output to channel if appropriate */
X if (!holdmsg && (Verbose || msg[0] != '0'))
X {
X (void) fflush(stdout);
X if (OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
X fprintf(OutChannel, "%s\r\n", msg);
X else
X fprintf(OutChannel, "%s\n", &msg[4]);
X (void) fflush(OutChannel);
X }
X}
X/*
X** PUTERRMSG -- like putmsg, but does special processing for error messages
X**
X** Parameters:
X** msg -- the message to output.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Sets the fatal error bit in the envelope as appropriate.
X*/
X
Xputerrmsg(msg)
X char *msg;
X{
X /* output the message as usual */
X putmsg(msg, HoldErrs);
X
X /* signal the error */
X Errors++;
X if (msg[0] == '5')
X CurEnv->e_flags |= EF_FATALERRS;
X}
X/*
X** FMTMSG -- format a message into buffer.
X**
X** Parameters:
X** eb -- error buffer to get result.
X** to -- the recipient tag for this message.
X** num -- arpanet error number.
X** en -- the error number to display.
X** fmt -- format of string.
X** a, b, c, d, e -- arguments.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** none.
X*/
X
X/*VARARGS5*/
Xstatic
Xfmtmsg(eb, to, num, eno, fmt, a, b, c, d, e)
X register char *eb;
X char *to;
X char *num;
X int eno;
X char *fmt;
X{
X char del;
X
X /* output the reply code */
X if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
X {
X num = fmt;
X fmt += 4;
X }
X if (num[3] == '-')
X del = '-';
X else
X del = ' ';
X (void) sprintf(eb, "%3.3s%c", num, del);
X eb += 4;
X
X /* output the file name and line number */
X if (FileName != NULL)
X {
X (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
X eb += strlen(eb);
X }
X
X /* output the "to" person */
X if (to != NULL && to[0] != '\0')
X {
X (void) sprintf(eb, "%s... ", to);
X while (*eb != '\0')
X *eb++ &= 0177;
X }
X
X /* output the message */
X (void) sprintf(eb, fmt, a, b, c, d, e);
X while (*eb != '\0')
X *eb++ &= 0177;
X
X /* output the error code, if any */
X if (eno != 0)
X {
X extern char *errstring();
X
X (void) sprintf(eb, ": %s", errstring(eno));
X eb += strlen(eb);
X }
X}
X/*
X** ERRSTRING -- return string description of error code
X**
X** Parameters:
X** errno -- the error number to translate
X**
X** Returns:
X** A string description of errno.
X**
X** Side Effects:
X** none.
X*/
X
Xchar *
Xerrstring(errno)
X int errno;
X{
X extern char *sys_errlist[];
X extern int sys_nerr;
X static char buf[100];
X# ifdef SMTP
X extern char *SmtpPhase;
X# endif SMTP
X
X# ifdef DAEMON
X# ifdef VMUNIX
X /*
X ** Handle special network error codes.
X **
X ** These are 4.2/4.3bsd specific; they should be in daemon.c.
X */
X
X switch (errno)
X {
X case ETIMEDOUT:
X case ECONNRESET:
X (void) strcpy(buf, sys_errlist[errno]);
X if (SmtpPhase != NULL)
X {
X (void) strcat(buf, " during ");
X (void) strcat(buf, SmtpPhase);
X }
X if (CurHostName != NULL)
X {
X (void) strcat(buf, " with ");
X (void) strcat(buf, CurHostName);
X }
X return (buf);
X
X case EHOSTDOWN:
X if (CurHostName == NULL)
X break;
X (void) sprintf(buf, "Host %s is down", CurHostName);
X return (buf);
X
X case ECONNREFUSED:
X if (CurHostName == NULL)
X break;
X (void) sprintf(buf, "Connection refused by %s", CurHostName);
X return (buf);
X
X case (TRY_AGAIN+MAX_ERRNO):
X (void) sprintf(buf, "Host Name Lookup Failure");
X return (buf);
X }
X# endif VMUNIX
X# endif DAEMON
X
X if (errno > 0 && errno < sys_nerr)
X return (sys_errlist[errno]);
X
X (void) sprintf(buf, "Error %d", errno);
X return (buf);
X}
END_OF_FILE
if test 7229 -ne `wc -c <'src/err.c'`; then
echo shar: \"'src/err.c'\" unpacked with wrong size!
fi
# end of 'src/err.c'
fi
if test -f 'src/macro.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/macro.c'\"
else
echo shar: Extracting \"'src/macro.c'\" \(4832 characters\)
sed "s/^X//" >'src/macro.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[] = "@(#)macro.c 5.3 (Berkeley) 9/19/85";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X** EXPAND -- macro expand a string using $x escapes.
X**
X** Parameters:
X** s -- the string to expand.
X** buf -- the place to put the expansion.
X** buflim -- the buffer limit, i.e., the address
X** of the last usable position in buf.
X** e -- envelope in which to work.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** none.
X*/
X
Xexpand(s, buf, buflim, e)
X register char *s;
X register char *buf;
X char *buflim;
X register ENVELOPE *e;
X{
X register char *xp;
X register char *q;
X bool skipping; /* set if conditionally skipping output */
X bool recurse = FALSE; /* set if recursion required */
X int i;
X char xbuf[BUFSIZ];
X extern char *macvalue();
X
X# ifdef DEBUG
X if (tTd(35, 24))
X {
X printf("expand(");
X xputs(s);
X printf(")\n");
X }
X# endif DEBUG
X
X skipping = FALSE;
X if (s == NULL)
X s = "";
X for (xp = xbuf; *s != '\0'; s++)
X {
X char c;
X
X /*
X ** Check for non-ordinary (special?) character.
X ** 'q' will be the interpolated quantity.
X */
X
X q = NULL;
X c = *s;
X switch (c)
X {
X case CONDIF: /* see if var set */
X c = *++s;
X skipping = macvalue(c, e) == NULL;
X continue;
X
X case CONDELSE: /* change state of skipping */
X skipping = !skipping;
X continue;
X
X case CONDFI: /* stop skipping */
X skipping = FALSE;
X continue;
X
X case '\001': /* macro interpolation */
X c = *++s;
X q = macvalue(c & 0177, e);
X if (q == NULL)
X continue;
X break;
X }
X
X /*
X ** Interpolate q or output one character
X */
X
X if (skipping || xp >= &xbuf[sizeof xbuf])
X continue;
X if (q == NULL)
X *xp++ = c;
X else
X {
X /* copy to end of q or max space remaining in buf */
X while ((c = *q++) != '\0' && xp < &xbuf[sizeof xbuf - 1])
X {
X if (iscntrl(c) && !isspace(c))
X recurse = TRUE;
X *xp++ = c;
X }
X }
X }
X *xp = '\0';
X
X# ifdef DEBUG
X if (tTd(35, 24))
X {
X printf("expand ==> ");
X xputs(xbuf);
X printf("\n");
X }
X# endif DEBUG
X
X /* recurse as appropriate */
X if (recurse)
X {
X expand(xbuf, buf, buflim, e);
X return;
X }
X
X /* copy results out */
X i = buflim - buf - 1;
X if (i > xp - xbuf)
X i = xp - xbuf;
X bcopy(xbuf, buf, i);
X buf[i] = '\0';
X}
X/*
X** DEFINE -- define a macro.
X**
X** this would be better done using a #define macro.
X**
X** Parameters:
X** n -- the macro name.
X** v -- the macro value.
X** e -- the envelope to store the definition in.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** e->e_macro[n] is defined.
X**
X** Notes:
X** There is one macro for each ASCII character,
X** although they are not all used. The currently
X** defined macros are:
X**
X** $a date in ARPANET format (preferring the Date: line
X** of the message)
X** $b the current date (as opposed to the date as found
X** the message) in ARPANET format
X** $c hop count
X** $d (current) date in UNIX (ctime) format
X** $e the SMTP entry message+
X** $f raw from address
X** $g translated from address
X** $h to host
X** $i queue id
X** $j official SMTP hostname, used in messages+
X** $l UNIX-style from line+
X** $n name of sendmail ("MAILER-DAEMON" on local
X** net typically)+
X** $o delimiters ("operators") for address tokens+
X** $p my process id in decimal
X** $q the string that becomes an address -- this is
X** normally used to combine $g & $x.
X** $r protocol used to talk to sender
X** $s sender's host name
X** $t the current time in seconds since 1/1/1970
X** $u to user
X** $v version number of sendmail
X** $w our host name (if it can be determined)
X** $x signature (full name) of from person
X** $y the tty id of our terminal
X** $z home directory of to person
X**
X** Macros marked with + must be defined in the
X** configuration file and are used internally, but
X** are not set.
X**
X** There are also some macros that can be used
X** arbitrarily to make the configuration file
X** cleaner. In general all upper-case letters
X** are available.
X*/
X
Xdefine(n, v, e)
X char n;
X char *v;
X register ENVELOPE *e;
X{
X# ifdef DEBUG
X if (tTd(35, 9))
X {
X printf("define(%c as ", n);
X xputs(v);
X printf(")\n");
X }
X# endif DEBUG
X e->e_macro[n & 0177] = v;
X}
X/*
X** MACVALUE -- return uninterpreted value of a macro.
X**
X** Parameters:
X** n -- the name of the macro.
X**
X** Returns:
X** The value of n.
X**
X** Side Effects:
X** none.
X*/
X
Xchar *
Xmacvalue(n, e)
X char n;
X register ENVELOPE *e;
X{
X n &= 0177;
X while (e != NULL)
X {
X register char *p = e->e_macro[n];
X
X if (p != NULL)
X return (p);
X e = e->e_parent;
X }
X return (NULL);
X}
END_OF_FILE
if test 4832 -ne `wc -c <'src/macro.c'`; then
echo shar: \"'src/macro.c'\" unpacked with wrong size!
fi
# end of 'src/macro.c'
fi
if test -f 'src/usersmtp.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/usersmtp.c'\"
else
echo shar: Extracting \"'src/usersmtp.c'\" \(10561 characters\)
sed "s/^X//" >'src/usersmtp.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
X# include <sysexits.h>
X# include <errno.h>
X# include "sendmail.h"
X
X# ifndef SMTP
X# ifndef lint
Xstatic char SccsId[] = "@(#)usersmtp.c 5.8 (Berkeley) 12/17/86 (no SMTP)";
X# endif not lint
X# else SMTP
X
X# ifndef lint
Xstatic char SccsId[] = "@(#)usersmtp.c 5.8 (Berkeley) 12/17/86";
X# endif not lint
X
X
X
X/*
X** USERSMTP -- run SMTP protocol from the user end.
X**
X** This protocol is described in RFC821.
X*/
X
X#define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
X#define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
X#define SMTPCLOSING 421 /* "Service Shutting Down" */
X
Xchar SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
Xchar SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
Xchar SmtpError[MAXLINE] = ""; /* save failure error messages */
XFILE *SmtpOut; /* output file */
XFILE *SmtpIn; /* input file */
Xint SmtpPid; /* pid of mailer */
X
X/* following represents the state of the SMTP connection */
Xint SmtpState; /* connection state, see below */
X
X#define SMTP_CLOSED 0 /* connection is closed */
X#define SMTP_OPEN 1 /* connection is open for business */
X#define SMTP_SSD 2 /* service shutting down */
X/*
X** SMTPINIT -- initialize SMTP.
X**
X** Opens the connection and sends the initial protocol.
X**
X** Parameters:
X** m -- mailer to create connection to.
X** pvp -- pointer to parameter vector to pass to
X** the mailer.
X**
X** Returns:
X** appropriate exit status -- EX_OK on success.
X** If not EX_OK, it should close the connection.
X**
X** Side Effects:
X** creates connection and sends initial protocol.
X*/
X
Xjmp_buf CtxGreeting;
X
Xsmtpinit(m, pvp)
X struct mailer *m;
X char **pvp;
X{
X register int r;
X EVENT *gte;
X char buf[MAXNAME];
X extern greettimeout();
X
X /*
X ** Open the connection to the mailer.
X */
X
X#ifdef DEBUG
X if (SmtpState == SMTP_OPEN)
X syserr("smtpinit: already open");
X#endif DEBUG
X
X SmtpIn = SmtpOut = NULL;
X SmtpState = SMTP_CLOSED;
X SmtpError[0] = '\0';
X SmtpPhase = "user open";
X SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
X if (SmtpPid < 0)
X {
X# ifdef DEBUG
X if (tTd(18, 1))
X printf("smtpinit: cannot open %s: stat %d errno %d\n",
X pvp[0], ExitStat, errno);
X# endif DEBUG
X if (CurEnv->e_xfp != NULL)
X {
X register char *p;
X extern char *errstring();
X extern char *statstring();
X
X if (errno == 0)
X {
X p = statstring(ExitStat);
X fprintf(CurEnv->e_xfp,
X "%.3s %s.%s... %s\n",
X p, pvp[1], m->m_name, p);
X }
X else
X {
X fprintf(CurEnv->e_xfp,
X "421 %s.%s... Deferred: %s\n",
X pvp[1], m->m_name, errstring(errno));
X }
X }
X return (ExitStat);
X }
X SmtpState = SMTP_OPEN;
X
X /*
X ** Get the greeting message.
X ** This should appear spontaneously. Give it five minutes to
X ** happen.
X */
X
X if (setjmp(CtxGreeting) != 0)
X goto tempfail;
X gte = setevent((time_t) 300, greettimeout, 0);
X SmtpPhase = "greeting wait";
X r = reply(m);
X clrevent(gte);
X if (r < 0 || REPLYTYPE(r) != 2)
X goto tempfail;
X
X /*
X ** Send the HELO command.
X ** My mother taught me to always introduce myself.
X */
X
X smtpmessage("HELO %s", m, MyHostName);
X SmtpPhase = "HELO wait";
X r = reply(m);
X if (r < 0)
X goto tempfail;
X else if (REPLYTYPE(r) == 5)
X goto unavailable;
X else if (REPLYTYPE(r) != 2)
X goto tempfail;
X
X /*
X ** If this is expected to be another sendmail, send some internal
X ** commands.
X */
X
X if (bitnset(M_INTERNAL, m->m_flags))
X {
X /* tell it to be verbose */
X smtpmessage("VERB", m);
X r = reply(m);
X if (r < 0)
X goto tempfail;
X
X /* tell it we will be sending one transaction only */
X smtpmessage("ONEX", m);
X r = reply(m);
X if (r < 0)
X goto tempfail;
X }
X
X /*
X ** Send the MAIL command.
X ** Designates the sender.
X */
X
X expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
X if (CurEnv->e_from.q_mailer == LocalMailer ||
X !bitnset(M_FROMPATH, m->m_flags))
X {
X smtpmessage("MAIL From:<%s>", m, buf);
X }
X else
X {
X smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
X buf[0] == '@' ? ',' : ':', buf);
X }
X SmtpPhase = "MAIL wait";
X r = reply(m);
X if (r < 0 || REPLYTYPE(r) == 4)
X goto tempfail;
X else if (r == 250)
X return (EX_OK);
X else if (r == 552)
X goto unavailable;
X
X /* protocol error -- close up */
X smtpquit(m);
X return (EX_PROTOCOL);
X
X /* signal a temporary failure */
X tempfail:
X smtpquit(m);
X return (EX_TEMPFAIL);
X
X /* signal service unavailable */
X unavailable:
X smtpquit(m);
X return (EX_UNAVAILABLE);
X}
X
X
Xstatic
Xgreettimeout()
X{
X /* timeout reading the greeting message */
X longjmp(CtxGreeting, 1);
X}
X/*
X** SMTPRCPT -- designate recipient.
X**
X** Parameters:
X** to -- address of recipient.
X** m -- the mailer we are sending to.
X**
X** Returns:
X** exit status corresponding to recipient status.
X**
X** Side Effects:
X** Sends the mail via SMTP.
X*/
X
Xsmtprcpt(to, m)
X ADDRESS *to;
X register MAILER *m;
X{
X register int r;
X extern char *remotename();
X
X smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
X
X SmtpPhase = "RCPT wait";
X r = reply(m);
X if (r < 0 || REPLYTYPE(r) == 4)
X return (EX_TEMPFAIL);
X else if (REPLYTYPE(r) == 2)
X return (EX_OK);
X else if (r == 550 || r == 551 || r == 553)
X return (EX_NOUSER);
X else if (r == 552 || r == 554)
X return (EX_UNAVAILABLE);
X return (EX_PROTOCOL);
X}
X/*
X** SMTPDATA -- send the data and clean up the transaction.
X**
X** Parameters:
X** m -- mailer being sent to.
X** e -- the envelope for this message.
X**
X** Returns:
X** exit status corresponding to DATA command.
X**
X** Side Effects:
X** none.
X*/
X
Xsmtpdata(m, e)
X struct mailer *m;
X register ENVELOPE *e;
X{
X register int r;
X
X /*
X ** Send the data.
X ** First send the command and check that it is ok.
X ** Then send the data.
X ** Follow it up with a dot to terminate.
X ** Finally get the results of the transaction.
X */
X
X /* send the command and check ok to proceed */
X smtpmessage("DATA", m);
X SmtpPhase = "DATA wait";
X r = reply(m);
X if (r < 0 || REPLYTYPE(r) == 4)
X return (EX_TEMPFAIL);
X else if (r == 554)
X return (EX_UNAVAILABLE);
X else if (r != 354)
X return (EX_PROTOCOL);
X
X /* now output the actual message */
X (*e->e_puthdr)(SmtpOut, m, CurEnv);
X putline("\n", SmtpOut, m);
X (*e->e_putbody)(SmtpOut, m, CurEnv);
X
X /* terminate the message */
X fprintf(SmtpOut, ".%s", m->m_eol);
X if (Verbose && !HoldErrs)
X nmessage(Arpa_Info, ">>> .");
X
X /* check for the results of the transaction */
X SmtpPhase = "result wait";
X r = reply(m);
X if (r < 0 || REPLYTYPE(r) == 4)
X return (EX_TEMPFAIL);
X else if (r == 250)
X return (EX_OK);
X else if (r == 552 || r == 554)
X return (EX_UNAVAILABLE);
X return (EX_PROTOCOL);
X}
X/*
X** SMTPQUIT -- close the SMTP connection.
X**
X** Parameters:
X** m -- a pointer to the mailer.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** sends the final protocol and closes the connection.
X*/
X
Xsmtpquit(m)
X register MAILER *m;
X{
X int i;
X
X /* if the connection is already closed, don't bother */
X if (SmtpIn == NULL)
X return;
X
X /* send the quit message if not a forced quit */
X if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
X {
X smtpmessage("QUIT", m);
X (void) reply(m);
X if (SmtpState == SMTP_CLOSED)
X return;
X }
X
X /* now actually close the connection */
X (void) fclose(SmtpIn);
X (void) fclose(SmtpOut);
X SmtpIn = SmtpOut = NULL;
X SmtpState = SMTP_CLOSED;
X
X /* and pick up the zombie */
X i = endmailer(SmtpPid, m->m_argv[0]);
X if (i != EX_OK)
X syserr("smtpquit %s: stat %d", m->m_argv[0], i);
X}
X/*
X** REPLY -- read arpanet reply
X**
X** Parameters:
X** m -- the mailer we are reading the reply from.
X**
X** Returns:
X** reply code it reads.
X**
X** Side Effects:
X** flushes the mail file.
X*/
X
Xreply(m)
X MAILER *m;
X{
X (void) fflush(SmtpOut);
X
X if (tTd(18, 1))
X printf("reply\n");
X
X /*
X ** Read the input line, being careful not to hang.
X */
X
X for (;;)
X {
X register int r;
X register char *p;
X
X /* actually do the read */
X if (CurEnv->e_xfp != NULL)
X (void) fflush(CurEnv->e_xfp); /* for debugging */
X
X /* if we are in the process of closing just give the code */
X if (SmtpState == SMTP_CLOSED)
X return (SMTPCLOSING);
X
X /* get the line from the other side */
X p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
X if (p == NULL)
X {
X extern char MsgBuf[]; /* err.c */
X extern char Arpa_TSyserr[]; /* conf.c */
X
X /* if the remote end closed early, fake an error */
X if (errno == 0)
X# ifdef ECONNRESET
X errno = ECONNRESET;
X# else ECONNRESET
X errno = EPIPE;
X# endif ECONNRESET
X
X message(Arpa_TSyserr, "reply: read error");
X# ifdef DEBUG
X /* if debugging, pause so we can see state */
X if (tTd(18, 100))
X pause();
X# endif DEBUG
X# ifdef LOG
X syslog(LOG_ERR, "%s", &MsgBuf[4]);
X# endif LOG
X SmtpState = SMTP_CLOSED;
X smtpquit(m);
X return (-1);
X }
X fixcrlf(SmtpReplyBuffer, TRUE);
X
X if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
X {
X /* serious error -- log the previous command */
X if (SmtpMsgBuffer[0] != '\0')
X fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
X SmtpMsgBuffer[0] = '\0';
X
X /* now log the message as from the other side */
X fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
X }
X
X /* display the input for verbose mode */
X if (Verbose && !HoldErrs)
X nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
X
X /* if continuation is required, we can go on */
X if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
X continue;
X
X /* decode the reply code */
X r = atoi(SmtpReplyBuffer);
X
X /* extra semantics: 0xx codes are "informational" */
X if (r < 100)
X continue;
X
X /* reply code 421 is "Service Shutting Down" */
X if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
X {
X /* send the quit protocol */
X SmtpState = SMTP_SSD;
X smtpquit(m);
X }
X
X /* save temporary failure messages for posterity */
X if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
X (void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
X
X return (r);
X }
X}
X/*
X** SMTPMESSAGE -- send message to server
X**
X** Parameters:
X** f -- format
X** m -- the mailer to control formatting.
X** a, b, c -- parameters
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** writes message to SmtpOut.
X*/
X
X/*VARARGS1*/
Xsmtpmessage(f, m, a, b, c)
X char *f;
X MAILER *m;
X{
X (void) sprintf(SmtpMsgBuffer, f, a, b, c);
X if (tTd(18, 1) || (Verbose && !HoldErrs))
X nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
X if (SmtpOut != NULL)
X fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
X}
X
X# endif SMTP
END_OF_FILE
if test 10561 -ne `wc -c <'src/usersmtp.c'`; then
echo shar: \"'src/usersmtp.c'\" unpacked with wrong size!
fi
# end of 'src/usersmtp.c'
fi
echo shar: End of archive 2 \(of 8\).
cp /dev/null ark2isdone
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