Sendmail source for the 3B1/7300 (part 7/8)
David H. Brierley
dave at galaxia.Newport.RI.US
Sat Feb 25 12:29:43 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 7 (of 8)."
# Contents: src/main.c src/parseaddr.c
# Wrapped by dave at galaxia on Fri Feb 24 20:24:15 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/main.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/main.c'\"
else
echo shar: Extracting \"'src/main.c'\" \(21371 characters\)
sed "s/^X//" >'src/main.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
Xchar copyright[] =
X"@(#) Copyright (c) 1980 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif not lint
X
X#ifndef lint
Xstatic char SccsId[] = "@(#)main.c 5.13 (Berkeley) 3/24/87";
X#endif not lint
X
X# define _DEFINE
X# include <signal.h>
X# include <sgtty.h>
X# include "sendmail.h"
X
X# ifdef lint
Xchar edata, end;
X# endif lint
X
X/*
X** SENDMAIL -- Post mail to a set of destinations.
X**
X** This is the basic mail router. All user mail programs should
X** call this routine to actually deliver mail. Sendmail in
X** turn calls a bunch of mail servers that do the real work of
X** delivering the mail.
X**
X** Sendmail is driven by tables read in from /usr/lib/sendmail.cf
X** (read by readcf.c). Some more static configuration info,
X** including some code that you may want to tailor for your
X** installation, is in conf.c. You may also want to touch
X** daemon.c (if you have some other IPC mechanism), acct.c
X** (to change your accounting), names.c (to adjust the name
X** server mechanism).
X**
X** Usage:
X** /usr/lib/sendmail [flags] addr ...
X**
X** See the associated documentation for details.
X**
X** Author:
X** Eric Allman, UCB/INGRES (until 10/81)
X** Britton-Lee, Inc., purveyors of fine
X** database computers (from 11/81)
X** The support of the INGRES Project and Britton-Lee is
X** gratefully acknowledged. Britton-Lee in
X** particular had absolutely nothing to gain from
X** my involvement in this project.
X*/
X
X
X
X
X
Xint NextMailer; /* "free" index into Mailer struct */
Xchar *FullName; /* sender's full name */
XENVELOPE BlankEnvelope; /* a "blank" envelope */
XENVELOPE MainEnvelope; /* the envelope around the basic letter */
XADDRESS NullAddress = /* a null address */
X { "", "", "" };
X
X/*
X** Pointers for setproctitle.
X** This allows "ps" listings to give more useful information.
X** These must be kept out of BSS for frozen configuration files
X** to work.
X*/
X
X# ifdef SETPROCTITLE
Xchar **Argv = NULL; /* pointer to argument vector */
Xchar *LastArgv = NULL; /* end of argv */
X# endif SETPROCTITLE
X
X#ifdef DAEMON
X#ifndef SMTP
XERROR %%%% Cannot have daemon mode without SMTP %%%% ERROR
X#endif SMTP
X#endif DAEMON
X
X
X
X
X
X
Xmain(argc, argv, envp)
X int argc;
X char **argv;
X char **envp;
X{
X register char *p;
X char **av;
X extern int finis();
X extern char Version[];
X char *from;
X typedef int (*fnptr)();
X STAB *st;
X register int i;
X bool readconfig = TRUE;
X bool queuemode = FALSE; /* process queue requests */
X bool nothaw;
X static bool reenter = FALSE;
X char jbuf[30]; /* holds MyHostName */
X extern bool safefile();
X extern time_t convtime();
X extern putheader(), putbody();
X extern ENVELOPE *newenvelope();
X extern intsig();
X extern char **myhostname();
X extern char *arpadate();
X extern char **environ;
X
X /*
X ** Check to see if we reentered.
X ** This would normally happen if e_putheader or e_putbody
X ** were NULL when invoked.
X */
X
X if (reenter)
X {
X syserr("main: reentered!");
X abort();
X }
X reenter = TRUE;
X
X#ifdef notdef
X /*
X * the following line does not have the effect the author expected --
X * it makes the time always use eastern time EST, EDT -- allyn
X */
X
X /* Enforce use of local time */
X unsetenv("TZ");
X#endif
X
X /*
X ** Be sure we have enough file descriptors.
X ** But also be sure that 0, 1, & 2 are open.
X */
X
X i = open("/dev/null", 2);
X while (i >= 0 && i < 2)
X i = dup(i);
X for (i = 3; i < 50; i++)
X (void) close(i);
X errno = 0;
X
X /*
X ** Set default values for variables.
X ** These cannot be in initialized data space.
X */
X
X setdefaults();
X
X /* set up the blank envelope */
X BlankEnvelope.e_puthdr = putheader;
X BlankEnvelope.e_putbody = putbody;
X BlankEnvelope.e_xfp = NULL;
X STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
X CurEnv = &BlankEnvelope;
X STRUCTCOPY(NullAddress, MainEnvelope.e_from);
X
X /*
X ** Do a quick prescan of the argument list.
X ** We do this to find out if we can potentially thaw the
X ** configuration file. If not, we do the thaw now so that
X ** the argument processing applies to this run rather than
X ** to the run that froze the configuration.
X */
X
X argv[argc] = NULL;
X av = argv;
X nothaw = FALSE;
X while ((p = *++av) != NULL)
X {
X if (strncmp(p, "-C", 2) == 0)
X {
X ConfFile = &p[2];
X if (ConfFile[0] == '\0')
X ConfFile = "sendmail.cf";
X (void) setgid(getrgid());
X (void) setuid(getruid());
X nothaw = TRUE;
X }
X else if (strncmp(p, "-bz", 3) == 0)
X nothaw = TRUE;
X# ifdef DEBUG
X else if (strncmp(p, "-d", 2) == 0)
X {
X tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
X tTflag(&p[2]);
X setbuf(stdout, (char *) NULL);
X printf("Version %s\n", Version);
X }
X# endif DEBUG
X }
X if (!nothaw)
X readconfig = !thaw(FreezeFile);
X
X /* reset the environment after the thaw */
X for (i = 0; i < MAXUSERENVIRON && envp[i] != NULL; i++)
X UserEnviron[i] = newstr(envp[i]);
X UserEnviron[i] = NULL;
X environ = UserEnviron;
X
X# ifdef SETPROCTITLE
X /*
X ** Save start and extent of argv for setproctitle.
X */
X
X Argv = argv;
X if (i > 0)
X LastArgv = envp[i - 1] + strlen(envp[i - 1]);
X else
X LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
X# endif SETPROCTITLE
X
X /*
X ** Now do basic initialization
X */
X
X InChannel = stdin;
X OutChannel = stdout;
X if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X (void) signal(SIGINT, intsig);
X if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
X (void) signal(SIGHUP, intsig);
X (void) signal(SIGTERM, intsig);
X (void) signal(SIGPIPE, SIG_IGN);
X OldUmask = umask(0);
X OpMode = MD_DELIVER;
X MotherPid = getpid();
X# ifndef V6
X FullName = getenv("NAME");
X# endif V6
X
X# ifdef LOG
X openlog("sendmail", LOG_PID, LOG_MAIL);
X# endif LOG
X errno = 0;
X from = NULL;
X
X if (readconfig)
X {
X /* initialize some macros, etc. */
X initmacros();
X
X /* hostname */
X av = myhostname(jbuf, sizeof jbuf);
X if (jbuf[0] != '\0')
X {
X#ifdef DEBUG
X if (tTd(0, 4))
X printf("canonical name: %s\n", jbuf);
X#endif DEBUG
X p = newstr(jbuf);
X define('w', p, CurEnv);
X setclass('w', p);
X }
X while (av != NULL && *av != NULL)
X {
X#ifdef DEBUG
X if (tTd(0, 4))
X printf("\ta.k.a.: %s\n", *av);
X#endif DEBUG
X setclass('w', *av++);
X }
X
X /* version */
X define('v', Version, CurEnv);
X }
X
X /* current time */
X define('b', arpadate((char *) NULL), CurEnv);
X
X /*
X ** Crack argv.
X */
X
X av = argv;
X p = rindex(*av, '/');
X if (p++ == NULL)
X p = *av;
X if (strcmp(p, "newaliases") == 0)
X OpMode = MD_INITALIAS;
X else if (strcmp(p, "mailq") == 0)
X OpMode = MD_PRINT;
X else if (strcmp(p, "smtpd") == 0)
X OpMode = MD_DAEMON;
X while ((p = *++av) != NULL && p[0] == '-')
X {
X switch (p[1])
X {
X case 'b': /* operations mode */
X switch (p[2])
X {
X case MD_DAEMON:
X# ifndef DAEMON
X syserr("Daemon mode not implemented");
X break;
X# endif DAEMON
X case MD_SMTP:
X# ifndef SMTP
X syserr("I don't speak SMTP");
X break;
X# endif SMTP
X case MD_ARPAFTP:
X case MD_DELIVER:
X case MD_VERIFY:
X case MD_TEST:
X case MD_INITALIAS:
X case MD_PRINT:
X case MD_FREEZE:
X OpMode = p[2];
X break;
X
X default:
X syserr("Invalid operation mode %c", p[2]);
X break;
X }
X break;
X
X case 'C': /* select configuration file (already done) */
X break;
X
X# ifdef DEBUG
X case 'd': /* debugging -- redo in case frozen */
X tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
X tTflag(&p[2]);
X setbuf(stdout, (char *) NULL);
X break;
X# endif DEBUG
X
X case 'f': /* from address */
X case 'r': /* obsolete -f flag */
X p += 2;
X if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
X {
X p = *++av;
X if (p == NULL || *p == '-')
X {
X syserr("No \"from\" person");
X av--;
X break;
X }
X }
X if (from != NULL)
X {
X syserr("More than one \"from\" person");
X break;
X }
X from = newstr(p);
X break;
X
X case 'F': /* set full name */
X p += 2;
X if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
X {
X syserr("Bad -F flag");
X av--;
X break;
X }
X FullName = newstr(p);
X break;
X
X case 'h': /* hop count */
X p += 2;
X if (*p == '\0' && ((p = *++av) == NULL || !isdigit(*p)))
X {
X syserr("Bad hop count (%s)", p);
X av--;
X break;
X }
X CurEnv->e_hopcount = atoi(p);
X break;
X
X case 'n': /* don't alias */
X NoAlias = TRUE;
X break;
X
X case 'o': /* set option */
X setoption(p[2], &p[3], FALSE, TRUE);
X break;
X
X case 'q': /* run queue files at intervals */
X# ifdef QUEUE
X queuemode = TRUE;
X QueueIntvl = convtime(&p[2]);
X# else QUEUE
X syserr("I don't know about queues");
X# endif QUEUE
X break;
X
X case 't': /* read recipients from message */
X GrabTo = TRUE;
X break;
X
X /* compatibility flags */
X case 'c': /* connect to non-local mailers */
X case 'e': /* error message disposition */
X case 'i': /* don't let dot stop me */
X case 'm': /* send to me too */
X case 'T': /* set timeout interval */
X case 'v': /* give blow-by-blow description */
X setoption(p[1], &p[2], FALSE, TRUE);
X break;
X
X case 's': /* save From lines in headers */
X setoption('f', &p[2], FALSE, TRUE);
X break;
X
X# ifdef DBM
X case 'I': /* initialize alias DBM file */
X OpMode = MD_INITALIAS;
X break;
X# endif DBM
X }
X }
X
X /*
X ** Do basic initialization.
X ** Read system control file.
X ** Extract special fields for local use.
X */
X
X if (OpMode == MD_FREEZE || readconfig)
X readcf(ConfFile);
X
X switch (OpMode)
X {
X case MD_FREEZE:
X /* this is critical to avoid forgeries of the frozen config */
X (void) setgid(getgid());
X (void) setuid(getuid());
X
X /* freeze the configuration */
X freeze(FreezeFile);
X exit(EX_OK);
X
X case MD_INITALIAS:
X Verbose = TRUE;
X break;
X }
X
X /* do heuristic mode adjustment */
X if (Verbose)
X {
X /* turn off noconnect option */
X setoption('c', "F", TRUE, FALSE);
X
X /* turn on interactive delivery */
X setoption('d', "", TRUE, FALSE);
X }
X
X /* our name for SMTP codes */
X expand("\001j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
X MyHostName = jbuf;
X
X /* the indices of local and program mailers */
X st = stab("local", ST_MAILER, ST_FIND);
X if (st == NULL)
X syserr("No local mailer defined");
X else
X LocalMailer = st->s_mailer;
X st = stab("prog", ST_MAILER, ST_FIND);
X if (st == NULL)
X syserr("No prog mailer defined");
X else
X ProgMailer = st->s_mailer;
X
X /* operate in queue directory */
X if (chdir(QueueDir) < 0)
X {
X syserr("cannot chdir(%s)", QueueDir);
X exit(EX_SOFTWARE);
X }
X
X /*
X ** Do operation-mode-dependent initialization.
X */
X
X switch (OpMode)
X {
X case MD_PRINT:
X /* print the queue */
X#ifdef QUEUE
X dropenvelope(CurEnv);
X printqueue();
X exit(EX_OK);
X#else QUEUE
X usrerr("No queue to print");
X finis();
X#endif QUEUE
X
X case MD_INITALIAS:
X /* initialize alias database */
X initaliases(AliasFile, TRUE);
X exit(EX_OK);
X
X case MD_DAEMON:
X /* don't open alias database -- done in srvrsmtp */
X break;
X
X default:
X /* open the alias database */
X initaliases(AliasFile, FALSE);
X break;
X }
X
X# ifdef DEBUG
X if (tTd(0, 15))
X {
X /* print configuration table (or at least part of it) */
X printrules();
X for (i = 0; i < MAXMAILERS; i++)
X {
X register struct mailer *m = Mailer[i];
X int j;
X
X if (m == NULL)
X continue;
X printf("mailer %d (%s): P=%s S=%d R=%d M=%ld F=", i, m->m_name,
X m->m_mailer, m->m_s_rwset, m->m_r_rwset,
X m->m_maxsize);
X for (j = '\0'; j <= '\177'; j++)
X if (bitnset(j, m->m_flags))
X (void) putchar(j);
X printf(" E=");
X xputs(m->m_eol);
X printf("\n");
X }
X }
X# endif DEBUG
X
X /*
X ** Switch to the main envelope.
X */
X
X CurEnv = newenvelope(&MainEnvelope);
X MainEnvelope.e_flags = BlankEnvelope.e_flags;
X
X /*
X ** If test mode, read addresses from stdin and process.
X */
X
X if (OpMode == MD_TEST)
X {
X char buf[MAXLINE];
X
X printf("ADDRESS TEST MODE\nEnter <ruleset> <address>\n");
X for (;;)
X {
X register char **pvp;
X char *q;
X extern char *DelimChar;
X
X printf("> ");
X (void) fflush(stdout);
X if (fgets(buf, sizeof buf, stdin) == NULL)
X finis();
X for (p = buf; isspace(*p); *p++)
X continue;
X q = p;
X while (*p != '\0' && !isspace(*p))
X p++;
X if (*p == '\0')
X continue;
X *p = '\0';
X do
X {
X extern char **prescan();
X char pvpbuf[PSBUFSIZE];
X
X pvp = prescan(++p, ',', pvpbuf);
X if (pvp == NULL)
X continue;
X rewrite(pvp, 3);
X p = q;
X while (*p != '\0')
X {
X rewrite(pvp, atoi(p));
X while (*p != '\0' && *p++ != ',')
X continue;
X }
X } while (*(p = DelimChar) != '\0');
X }
X }
X
X# ifdef QUEUE
X /*
X ** If collecting stuff from the queue, go start doing that.
X */
X
X if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
X {
X runqueue(FALSE);
X finis();
X }
X# endif QUEUE
X
X /*
X ** If a daemon, wait for a request.
X ** getrequests will always return in a child.
X ** If we should also be processing the queue, start
X ** doing it in background.
X ** We check for any errors that might have happened
X ** during startup.
X */
X
X if (OpMode == MD_DAEMON || QueueIntvl != 0)
X {
X if (!tTd(0, 1))
X {
X /* put us in background */
X i = fork();
X if (i < 0)
X syserr("daemon: cannot fork");
X if (i != 0)
X exit(0);
X
X /* get our pid right */
X MotherPid = getpid();
X
X /* disconnect from our controlling tty */
X disconnect(TRUE);
X }
X
X# ifdef QUEUE
X if (queuemode)
X {
X runqueue(TRUE);
X if (OpMode != MD_DAEMON)
X for (;;)
X pause();
X }
X# endif QUEUE
X dropenvelope(CurEnv);
X
X#ifdef DAEMON
X getrequests();
X
X /* at this point we are in a child: reset state */
X OpMode = MD_SMTP;
X (void) newenvelope(CurEnv);
X openxscript(CurEnv);
X#endif DAEMON
X }
X
X# ifdef SMTP
X /*
X ** If running SMTP protocol, start collecting and executing
X ** commands. This will never return.
X */
X
X if (OpMode == MD_SMTP)
X smtp();
X# endif SMTP
X
X /*
X ** Do basic system initialization and set the sender
X */
X
X initsys();
X setsender(from);
X
X if (OpMode != MD_ARPAFTP && *av == NULL && !GrabTo)
X {
X usrerr("Recipient names must be specified");
X
X /* collect body for UUCP return */
X if (OpMode != MD_VERIFY)
X collect(FALSE);
X finis();
X }
X if (OpMode == MD_VERIFY)
X SendMode = SM_VERIFY;
X
X /*
X ** Scan argv and deliver the message to everyone.
X */
X
X sendtoargv(av);
X
X /* if we have had errors sofar, arrange a meaningful exit stat */
X if (Errors > 0 && ExitStat == EX_OK)
X ExitStat = EX_USAGE;
X
X /*
X ** Read the input mail.
X */
X
X CurEnv->e_to = NULL;
X if (OpMode != MD_VERIFY || GrabTo)
X collect(FALSE);
X errno = 0;
X
X /* collect statistics */
X if (OpMode != MD_VERIFY)
X markstats(CurEnv, (ADDRESS *) NULL);
X
X# ifdef DEBUG
X if (tTd(1, 1))
X printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
X# endif DEBUG
X
X /*
X ** Actually send everything.
X ** If verifying, just ack.
X */
X
X CurEnv->e_from.q_flags |= QDONTSEND;
X CurEnv->e_to = NULL;
X sendall(CurEnv, SM_DEFAULT);
X
X /*
X ** All done.
X */
X
X finis();
X}
X/*
X** FINIS -- Clean up and exit.
X**
X** Parameters:
X** none
X**
X** Returns:
X** never
X**
X** Side Effects:
X** exits sendmail
X*/
X
Xfinis()
X{
X# ifdef DEBUG
X if (tTd(2, 1))
X printf("\n====finis: stat %d e_flags %o\n", ExitStat, CurEnv->e_flags);
X# endif DEBUG
X
X /* clean up temp files */
X CurEnv->e_to = NULL;
X dropenvelope(CurEnv);
X
X /* post statistics */
X poststats(StatFile);
X
X /* and exit */
X# ifdef LOG
X if (LogLevel > 11)
X syslog(LOG_DEBUG, "finis, pid=%d", getpid());
X# endif LOG
X if (ExitStat == EX_TEMPFAIL)
X ExitStat = EX_OK;
X exit(ExitStat);
X}
X/*
X** INTSIG -- clean up on interrupt
X**
X** This just arranges to exit. It pessimises in that it
X** may resend a message.
X**
X** Parameters:
X** none.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Unlocks the current job.
X*/
X
Xintsig()
X{
X FileName = NULL;
X unlockqueue(CurEnv);
X exit(EX_OK);
X}
X/*
X** INITMACROS -- initialize the macro system
X**
X** This just involves defining some macros that are actually
X** used internally as metasymbols to be themselves.
X**
X** Parameters:
X** none.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** initializes several macros to be themselves.
X*/
X
Xstruct metamac
X{
X char metaname;
X char metaval;
X};
X
Xstruct metamac MetaMacros[] =
X{
X /* LHS pattern matching characters */
X '*', MATCHZANY, '+', MATCHANY, '-', MATCHONE, '=', MATCHCLASS,
X '~', MATCHNCLASS,
X
X /* these are RHS metasymbols */
X '#', CANONNET, '@', CANONHOST, ':', CANONUSER, '>', CALLSUBR,
X
X /* the conditional operations */
X '?', CONDIF, '|', CONDELSE, '.', CONDFI,
X
X /* and finally the hostname lookup characters */
X '[', HOSTBEGIN, ']', HOSTEND,
X
X '\0'
X};
X
Xinitmacros()
X{
X register struct metamac *m;
X char buf[5];
X register int c;
X
X for (m = MetaMacros; m->metaname != '\0'; m++)
X {
X buf[0] = m->metaval;
X buf[1] = '\0';
X define(m->metaname, newstr(buf), CurEnv);
X }
X buf[0] = MATCHREPL;
X buf[2] = '\0';
X for (c = '0'; c <= '9'; c++)
X {
X buf[1] = c;
X define(c, newstr(buf), CurEnv);
X }
X}
X/*
X** FREEZE -- freeze BSS & allocated memory
X**
X** This will be used to efficiently load the configuration file.
X**
X** Parameters:
X** freezefile -- the name of the file to freeze to.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Writes BSS and malloc'ed memory to freezefile
X*/
X
Xunion frz
X{
X char frzpad[BUFSIZ]; /* insure we are on a BUFSIZ boundary */
X struct
X {
X time_t frzstamp; /* timestamp on this freeze */
X char *frzbrk; /* the current break */
X char *frzedata; /* address of edata */
X char *frzend; /* address of end */
X char frzver[252]; /* sendmail version */
X } frzinfo;
X};
X
Xfreeze(freezefile)
X char *freezefile;
X{
X int f;
X union frz fhdr;
X extern char edata, end;
X extern char *sbrk();
X extern char Version[];
X
X if (freezefile == NULL)
X return;
X
X /* try to open the freeze file */
X f = creat(freezefile, FileMode);
X if (f < 0)
X {
X syserr("Cannot freeze");
X errno = 0;
X return;
X }
X
X /* build the freeze header */
X fhdr.frzinfo.frzstamp = curtime();
X fhdr.frzinfo.frzbrk = sbrk(0);
X fhdr.frzinfo.frzedata = &edata;
X fhdr.frzinfo.frzend = &end;
X (void) strcpy(fhdr.frzinfo.frzver, Version);
X
X /* write out the freeze header */
X if (write(f, (char *) &fhdr, sizeof fhdr) != sizeof fhdr ||
X write(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
X (int) (fhdr.frzinfo.frzbrk - &edata))
X {
X syserr("Cannot freeze");
X }
X
X /* fine, clean up */
X (void) close(f);
X}
X/*
X** THAW -- read in the frozen configuration file.
X**
X** Parameters:
X** freezefile -- the name of the file to thaw from.
X**
X** Returns:
X** TRUE if it successfully read the freeze file.
X** FALSE otherwise.
X**
X** Side Effects:
X** reads freezefile in to BSS area.
X*/
X
Xthaw(freezefile)
X char *freezefile;
X{
X int f;
X union frz fhdr;
X extern char edata;
X extern char Version[];
X extern caddr_t brk();
X
X if (freezefile == NULL)
X return (FALSE);
X
X /* open the freeze file */
X f = open(freezefile, 0);
X if (f < 0)
X {
X errno = 0;
X return (FALSE);
X }
X
X /* read in the header */
X if (read(f, (char *) &fhdr, sizeof fhdr) < sizeof fhdr ||
X fhdr.frzinfo.frzedata != &edata ||
X fhdr.frzinfo.frzend != &end ||
X strcmp(fhdr.frzinfo.frzver, Version) != 0)
X {
X (void) close(f);
X return (FALSE);
X }
X
X /* arrange to have enough space */
X if (brk(fhdr.frzinfo.frzbrk) == (caddr_t) -1)
X {
X syserr("Cannot break to %x", fhdr.frzinfo.frzbrk);
X (void) close(f);
X return (FALSE);
X }
X
X /* now read in the freeze file */
X if (read(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
X (int) (fhdr.frzinfo.frzbrk - &edata))
X {
X /* oops! we have trashed memory..... */
X (void) write(2, "Cannot read freeze file\n", 24);
X _exit(EX_SOFTWARE);
X }
X
X (void) close(f);
X return (TRUE);
X}
X/*
X** DISCONNECT -- remove our connection with any foreground process
X**
X** Parameters:
X** fulldrop -- if set, we should also drop the controlling
X** TTY if possible -- this should only be done when
X** setting up the daemon since otherwise UUCP can
X** leave us trying to open a dialin, and we will
X** wait for the carrier.
X**
X** Returns:
X** none
X**
X** Side Effects:
X** Trys to insure that we are immune to vagaries of
X** the controlling tty.
X*/
X
Xdisconnect(fulldrop)
X bool fulldrop;
X{
X int fd;
X
X#ifdef DEBUG
X if (tTd(52, 1))
X printf("disconnect: In %d Out %d\n", fileno(InChannel),
X fileno(OutChannel));
X if (tTd(52, 5))
X {
X printf("don't\n");
X return;
X }
X#endif DEBUG
X
X /* be sure we don't get nasty signals */
X (void) signal(SIGHUP, SIG_IGN);
X (void) signal(SIGINT, SIG_IGN);
X (void) signal(SIGQUIT, SIG_IGN);
X
X /* we can't communicate with our caller, so.... */
X HoldErrs = TRUE;
X ErrorMode = EM_MAIL;
X Verbose = FALSE;
X
X /* all input from /dev/null */
X if (InChannel != stdin)
X {
X (void) fclose(InChannel);
X InChannel = stdin;
X }
X (void) freopen("/dev/null", "r", stdin);
X
X /* output to the transcript */
X if (OutChannel != stdout)
X {
X (void) fclose(OutChannel);
X OutChannel = stdout;
X }
X if (CurEnv->e_xfp == NULL)
X CurEnv->e_xfp = fopen("/dev/null", "w");
X (void) fflush(stdout);
X (void) close(1);
X (void) close(2);
X while ((fd = dup(fileno(CurEnv->e_xfp))) < 2 && fd > 0)
X continue;
X
X#ifdef TIOCNOTTY
X /* drop our controlling TTY completely if possible */
X if (fulldrop)
X {
X fd = open("/dev/tty", 2);
X if (fd >= 0)
X {
X (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
X (void) close(fd);
X }
X (void) setpgrp(0, 0);
X errno = 0;
X }
X#endif TIOCNOTTY
X
X# ifdef LOG
X if (LogLevel > 11)
X syslog(LOG_DEBUG, "in background, pid=%d", getpid());
X# endif LOG
X
X errno = 0;
X}
END_OF_FILE
if test 21371 -ne `wc -c <'src/main.c'`; then
echo shar: \"'src/main.c'\" unpacked with wrong size!
fi
# end of 'src/main.c'
fi
if test -f 'src/parseaddr.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/parseaddr.c'\"
else
echo shar: Extracting \"'src/parseaddr.c'\" \(24032 characters\)
sed "s/^X//" >'src/parseaddr.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[] = "@(#)parseaddr.c 5.6 (Berkeley) 4/2/86";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X** PARSEADDR -- Parse an address
X**
X** Parses an address and breaks it up into three parts: a
X** net to transmit the message on, the host to transmit it
X** to, and a user on that host. These are loaded into an
X** ADDRESS header with the values squirreled away if necessary.
X** The "user" part may not be a real user; the process may
X** just reoccur on that machine. For example, on a machine
X** with an arpanet connection, the address
X** csvax.bill at berkeley
X** will break up to a "user" of 'csvax.bill' and a host
X** of 'berkeley' -- to be transmitted over the arpanet.
X**
X** Parameters:
X** addr -- the address to parse.
X** a -- a pointer to the address descriptor buffer.
X** If NULL, a header will be created.
X** copyf -- determines what shall be copied:
X** -1 -- don't copy anything. The printname
X** (q_paddr) is just addr, and the
X** user & host are allocated internally
X** to parse.
X** 0 -- copy out the parsed user & host, but
X** don't copy the printname.
X** +1 -- copy everything.
X** delim -- the character to terminate the address, passed
X** to prescan.
X**
X** Returns:
X** A pointer to the address descriptor header (`a' if
X** `a' is non-NULL).
X** NULL on error.
X**
X** Side Effects:
X** none
X*/
X
X/* following delimiters are inherent to the internal algorithms */
X# define DELIMCHARS "\001()<>,;\\\"\r\n" /* word delimiters */
X
XADDRESS *
Xparseaddr(addr, a, copyf, delim)
X char *addr;
X register ADDRESS *a;
X int copyf;
X char delim;
X{
X register char **pvp;
X register struct mailer *m;
X char pvpbuf[PSBUFSIZE];
X extern char **prescan();
X extern ADDRESS *buildaddr();
X
X /*
X ** Initialize and prescan address.
X */
X
X CurEnv->e_to = addr;
X# ifdef DEBUG
X if (tTd(20, 1))
X printf("\n--parseaddr(%s)\n", addr);
X# endif DEBUG
X
X pvp = prescan(addr, delim, pvpbuf);
X if (pvp == NULL)
X return (NULL);
X
X /*
X ** Apply rewriting rules.
X ** Ruleset 0 does basic parsing. It must resolve.
X */
X
X rewrite(pvp, 3);
X rewrite(pvp, 0);
X
X /*
X ** See if we resolved to a real mailer.
X */
X
X if (pvp[0][0] != CANONNET)
X {
X setstat(EX_USAGE);
X usrerr("cannot resolve name");
X return (NULL);
X }
X
X /*
X ** Build canonical address from pvp.
X */
X
X a = buildaddr(pvp, a);
X if (a == NULL)
X return (NULL);
X m = a->q_mailer;
X
X /*
X ** Make local copies of the host & user and then
X ** transport them out.
X */
X
X if (copyf > 0)
X {
X extern char *DelimChar;
X char savec = *DelimChar;
X
X *DelimChar = '\0';
X a->q_paddr = newstr(addr);
X *DelimChar = savec;
X }
X else
X a->q_paddr = addr;
X
X if (a->q_user == NULL)
X a->q_user = "";
X if (a->q_host == NULL)
X a->q_host = "";
X
X if (copyf >= 0)
X {
X a->q_host = newstr(a->q_host);
X if (a->q_user != a->q_paddr)
X a->q_user = newstr(a->q_user);
X }
X
X /*
X ** Convert host name to lower case if requested.
X ** User name will be done later.
X */
X
X if (!bitnset(M_HST_UPPER, m->m_flags))
X makelower(a->q_host);
X
X /*
X ** Compute return value.
X */
X
X# ifdef DEBUG
X if (tTd(20, 1))
X {
X printf("parseaddr-->");
X printaddr(a, FALSE);
X }
X# endif DEBUG
X
X return (a);
X}
X/*
X** LOWERADDR -- map UPPER->lower case on addresses as requested.
X**
X** Parameters:
X** a -- address to be mapped.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** none.
X*/
X
Xloweraddr(a)
X register ADDRESS *a;
X{
X register MAILER *m = a->q_mailer;
X
X if (!bitnset(M_USR_UPPER, m->m_flags))
X makelower(a->q_user);
X}
X/*
X** PRESCAN -- Prescan name and make it canonical
X**
X** Scans a name and turns it into a set of tokens. This process
X** deletes blanks and comments (in parentheses).
X**
X** This routine knows about quoted strings and angle brackets.
X**
X** There are certain subtleties to this routine. The one that
X** comes to mind now is that backslashes on the ends of names
X** are silently stripped off; this is intentional. The problem
X** is that some versions of sndmsg (like at LBL) set the kill
X** character to something other than @ when reading addresses;
X** so people type "csvax.eric\@berkeley" -- which screws up the
X** berknet mailer.
X**
X** Parameters:
X** addr -- the name to chomp.
X** delim -- the delimiter for the address, normally
X** '\0' or ','; \0 is accepted in any case.
X** If '\t' then we are reading the .cf file.
X** pvpbuf -- place to put the saved text -- note that
X** the pointers are static.
X**
X** Returns:
X** A pointer to a vector of tokens.
X** NULL on error.
X**
X** Side Effects:
X** sets DelimChar to point to the character matching 'delim'.
X*/
X
X/* states and character types */
X# define OPR 0 /* operator */
X# define ATM 1 /* atom */
X# define QST 2 /* in quoted string */
X# define SPC 3 /* chewing up spaces */
X# define ONE 4 /* pick up one character */
X
X# define NSTATES 5 /* number of states */
X# define TYPE 017 /* mask to select state type */
X
X/* meta bits for table */
X# define M 020 /* meta character; don't pass through */
X# define B 040 /* cause a break */
X# define MB M|B /* meta-break */
X
Xstatic short StateTab[NSTATES][NSTATES] =
X{
X /* oldst chtype> OPR ATM QST SPC ONE */
X /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B,
X /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B,
X /*QST*/ QST, QST, OPR, QST, QST,
X /*SPC*/ OPR, ATM, QST, SPC|M, ONE,
X /*ONE*/ OPR, OPR, OPR, OPR, OPR,
X};
X
X# define NOCHAR -1 /* signal nothing in lookahead token */
X
Xchar *DelimChar; /* set to point to the delimiter */
X
Xchar **
Xprescan(addr, delim, pvpbuf)
X char *addr;
X char delim;
X char pvpbuf[];
X{
X register char *p;
X register char *q;
X register int c;
X char **avp;
X bool bslashmode;
X int cmntcnt;
X int anglecnt;
X char *tok;
X int state;
X int newstate;
X static char *av[MAXATOM+1];
X extern int errno;
X
X /* make sure error messages don't have garbage on them */
X errno = 0;
X
X q = pvpbuf;
X bslashmode = FALSE;
X cmntcnt = 0;
X anglecnt = 0;
X avp = av;
X state = OPR;
X c = NOCHAR;
X p = addr;
X# ifdef DEBUG
X if (tTd(22, 45))
X {
X printf("prescan: ");
X xputs(p);
X (void) putchar('\n');
X }
X# endif DEBUG
X
X do
X {
X /* read a token */
X tok = q;
X for (;;)
X {
X /* store away any old lookahead character */
X if (c != NOCHAR)
X {
X /* see if there is room */
X if (q >= &pvpbuf[PSBUFSIZE - 5])
X {
X usrerr("Address too long");
X DelimChar = p;
X return (NULL);
X }
X
X /* squirrel it away */
X *q++ = c;
X }
X
X /* read a new input character */
X c = *p++;
X if (c == '\0')
X break;
X c &= ~0200;
X
X# ifdef DEBUG
X if (tTd(22, 101))
X printf("c=%c, s=%d; ", c, state);
X# endif DEBUG
X
X /* chew up special characters */
X *q = '\0';
X if (bslashmode)
X {
X /* kludge \! for naive users */
X if (c != '!')
X c |= 0200;
X bslashmode = FALSE;
X }
X else if (c == '\\')
X {
X bslashmode = TRUE;
X c = NOCHAR;
X }
X else if (state == QST)
X {
X /* do nothing, just avoid next clauses */
X }
X else if (c == '(')
X {
X cmntcnt++;
X c = NOCHAR;
X }
X else if (c == ')')
X {
X if (cmntcnt <= 0)
X {
X usrerr("Unbalanced ')'");
X DelimChar = p;
X return (NULL);
X }
X else
X cmntcnt--;
X }
X else if (cmntcnt > 0)
X c = NOCHAR;
X else if (c == '<')
X anglecnt++;
X else if (c == '>')
X {
X if (anglecnt <= 0)
X {
X usrerr("Unbalanced '>'");
X DelimChar = p;
X return (NULL);
X }
X anglecnt--;
X }
X else if (delim == ' ' && isspace(c))
X c = ' ';
X
X if (c == NOCHAR)
X continue;
X
X /* see if this is end of input */
X if (c == delim && anglecnt <= 0 && state != QST)
X break;
X
X newstate = StateTab[state][toktype(c)];
X# ifdef DEBUG
X if (tTd(22, 101))
X printf("ns=%02o\n", newstate);
X# endif DEBUG
X state = newstate & TYPE;
X if (bitset(M, newstate))
X c = NOCHAR;
X if (bitset(B, newstate))
X break;
X }
X
X /* new token */
X if (tok != q)
X {
X *q++ = '\0';
X# ifdef DEBUG
X if (tTd(22, 36))
X {
X printf("tok=");
X xputs(tok);
X (void) putchar('\n');
X }
X# endif DEBUG
X if (avp >= &av[MAXATOM])
X {
X syserr("prescan: too many tokens");
X DelimChar = p;
X return (NULL);
X }
X *avp++ = tok;
X }
X } while (c != '\0' && (c != delim || anglecnt > 0));
X *avp = NULL;
X DelimChar = --p;
X if (cmntcnt > 0)
X usrerr("Unbalanced '('");
X else if (anglecnt > 0)
X usrerr("Unbalanced '<'");
X else if (state == QST)
X usrerr("Unbalanced '\"'");
X else if (av[0] != NULL)
X return (av);
X return (NULL);
X}
X/*
X** TOKTYPE -- return token type
X**
X** Parameters:
X** c -- the character in question.
X**
X** Returns:
X** Its type.
X**
X** Side Effects:
X** none.
X*/
X
Xtoktype(c)
X register char c;
X{
X static char buf[50];
X static bool firstime = TRUE;
X
X if (firstime)
X {
X firstime = FALSE;
X expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
X (void) strcat(buf, DELIMCHARS);
X }
X if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
X return (ONE);
X if (c == '"')
X return (QST);
X if (!isascii(c))
X return (ATM);
X if (isspace(c) || c == ')')
X return (SPC);
X if (iscntrl(c) || index(buf, c) != NULL)
X return (OPR);
X return (ATM);
X}
X/*
X** REWRITE -- apply rewrite rules to token vector.
X**
X** This routine is an ordered production system. Each rewrite
X** rule has a LHS (called the pattern) and a RHS (called the
X** rewrite); 'rwr' points the the current rewrite rule.
X**
X** For each rewrite rule, 'avp' points the address vector we
X** are trying to match against, and 'pvp' points to the pattern.
X** If pvp points to a special match value (MATCHZANY, MATCHANY,
X** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
X** matched is saved away in the match vector (pointed to by 'mvp').
X**
X** When a match between avp & pvp does not match, we try to
X** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
X** we must also back out the match in mvp. If we reach a
X** MATCHANY or MATCHZANY we just extend the match and start
X** over again.
X**
X** When we finally match, we rewrite the address vector
X** and try over again.
X**
X** Parameters:
X** pvp -- pointer to token vector.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** pvp is modified.
X*/
X
Xstruct match
X{
X char **first; /* first token matched */
X char **last; /* last token matched */
X};
X
X# define MAXMATCH 9 /* max params per rewrite */
X
X
Xrewrite(pvp, ruleset)
X char **pvp;
X int ruleset;
X{
X register char *ap; /* address pointer */
X register char *rp; /* rewrite pointer */
X register char **avp; /* address vector pointer */
X register char **rvp; /* rewrite vector pointer */
X register struct match *mlp; /* cur ptr into mlist */
X register struct rewrite *rwr; /* pointer to current rewrite rule */
X struct match mlist[MAXMATCH]; /* stores match on LHS */
X char *npvp[MAXATOM+1]; /* temporary space for rebuild */
X extern bool sameword();
X
X if (OpMode == MD_TEST || tTd(21, 2))
X {
X printf("rewrite: ruleset %2d input:", ruleset);
X printav(pvp);
X }
X if (pvp == NULL)
X return;
X
X /*
X ** Run through the list of rewrite rules, applying
X ** any that match.
X */
X
X for (rwr = RewriteRules[ruleset]; rwr != NULL; )
X {
X# ifdef DEBUG
X if (tTd(21, 12))
X {
X printf("-----trying rule:");
X printav(rwr->r_lhs);
X }
X# endif DEBUG
X
X /* try to match on this rule */
X mlp = mlist;
X rvp = rwr->r_lhs;
X avp = pvp;
X while ((ap = *avp) != NULL || *rvp != NULL)
X {
X rp = *rvp;
X# ifdef DEBUG
X if (tTd(21, 35))
X {
X printf("ap=");
X xputs(ap);
X printf(", rp=");
X xputs(rp);
X printf("\n");
X }
X# endif DEBUG
X if (rp == NULL)
X {
X /* end-of-pattern before end-of-address */
X goto backup;
X }
X if (ap == NULL && *rp != MATCHZANY)
X {
X /* end-of-input */
X break;
X }
X
X switch (*rp)
X {
X register STAB *s;
X
X case MATCHCLASS:
X case MATCHNCLASS:
X /* match any token in (not in) a class */
X s = stab(ap, ST_CLASS, ST_FIND);
X if (s == NULL || !bitnset(rp[1], s->s_class))
X {
X if (*rp == MATCHCLASS)
X goto backup;
X }
X else if (*rp == MATCHNCLASS)
X goto backup;
X
X /* explicit fall-through */
X
X case MATCHONE:
X case MATCHANY:
X /* match exactly one token */
X mlp->first = avp;
X mlp->last = avp++;
X mlp++;
X break;
X
X case MATCHZANY:
X /* match zero or more tokens */
X mlp->first = avp;
X mlp->last = avp - 1;
X mlp++;
X break;
X
X default:
X /* must have exact match */
X if (!sameword(rp, ap))
X goto backup;
X avp++;
X break;
X }
X
X /* successful match on this token */
X rvp++;
X continue;
X
X backup:
X /* match failed -- back up */
X while (--rvp >= rwr->r_lhs)
X {
X rp = *rvp;
X if (*rp == MATCHANY || *rp == MATCHZANY)
X {
X /* extend binding and continue */
X avp = ++mlp[-1].last;
X avp++;
X rvp++;
X break;
X }
X avp--;
X if (*rp == MATCHONE || *rp == MATCHCLASS ||
X *rp == MATCHNCLASS)
X {
X /* back out binding */
X mlp--;
X }
X }
X
X if (rvp < rwr->r_lhs)
X {
X /* total failure to match */
X break;
X }
X }
X
X /*
X ** See if we successfully matched
X */
X
X if (rvp < rwr->r_lhs || *rvp != NULL)
X {
X# ifdef DEBUG
X if (tTd(21, 10))
X printf("----- rule fails\n");
X# endif DEBUG
X rwr = rwr->r_next;
X continue;
X }
X
X rvp = rwr->r_rhs;
X# ifdef DEBUG
X if (tTd(21, 12))
X {
X printf("-----rule matches:");
X printav(rvp);
X }
X# endif DEBUG
X
X rp = *rvp;
X if (*rp == CANONUSER)
X {
X rvp++;
X rwr = rwr->r_next;
X }
X else if (*rp == CANONHOST)
X {
X rvp++;
X rwr = NULL;
X }
X else if (*rp == CANONNET)
X rwr = NULL;
X
X /* substitute */
X for (avp = npvp; *rvp != NULL; rvp++)
X {
X register struct match *m;
X register char **pp;
X
X rp = *rvp;
X if (*rp == MATCHREPL)
X {
X /* substitute from LHS */
X m = &mlist[rp[1] - '1'];
X if (m >= mlp)
X {
X syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
X return;
X }
X# ifdef DEBUG
X if (tTd(21, 15))
X {
X printf("$%c:", rp[1]);
X pp = m->first;
X while (pp <= m->last)
X {
X printf(" %x=\"", *pp);
X (void) fflush(stdout);
X printf("%s\"", *pp++);
X }
X printf("\n");
X }
X# endif DEBUG
X pp = m->first;
X while (pp <= m->last)
X {
X if (avp >= &npvp[MAXATOM])
X {
X syserr("rewrite: expansion too long");
X return;
X }
X *avp++ = *pp++;
X }
X }
X else
X {
X /* vanilla replacement */
X if (avp >= &npvp[MAXATOM])
X {
X toolong:
X syserr("rewrite: expansion too long");
X return;
X }
X *avp++ = rp;
X }
X }
X *avp++ = NULL;
X
X /*
X ** Check for any hostname lookups.
X */
X
X for (rvp = npvp; *rvp != NULL; rvp++)
X {
X char **hbrvp;
X char **xpvp;
X int trsize;
X char *olddelimchar;
X char buf[MAXNAME + 1];
X char *pvpb1[MAXATOM + 1];
X char pvpbuf[PSBUFSIZE];
X extern char *DelimChar;
X
X if (**rvp != HOSTBEGIN)
X continue;
X
X /*
X ** Got a hostname lookup.
X **
X ** This could be optimized fairly easily.
X */
X
X hbrvp = rvp;
X
X /* extract the match part */
X while (*++rvp != NULL && **rvp != HOSTEND)
X continue;
X if (*rvp != NULL)
X *rvp++ = NULL;
X
X /* save the remainder of the input string */
X trsize = (int) (avp - rvp + 1) * sizeof *rvp;
X bcopy((char *) rvp, (char *) pvpb1, trsize);
X
X /* look it up */
X cataddr(++hbrvp, buf, sizeof buf);
X maphostname(buf, sizeof buf);
X
X /* scan the new host name */
X olddelimchar = DelimChar;
X xpvp = prescan(buf, '\0', pvpbuf);
X DelimChar = olddelimchar;
X if (xpvp == NULL)
X {
X syserr("rewrite: cannot prescan canonical hostname: %s", buf);
X return;
X }
X
X /* append it to the token list */
X for (avp = --hbrvp; *xpvp != NULL; xpvp++)
X {
X *avp++ = newstr(*xpvp);
X if (avp >= &npvp[MAXATOM])
X goto toolong;
X }
X
X /* restore the old trailing information */
X for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
X if (avp >= &npvp[MAXATOM])
X goto toolong;
X
X break;
X }
X
X /*
X ** Check for subroutine calls.
X */
X
X if (*npvp != NULL && **npvp == CALLSUBR)
X {
X bcopy((char *) &npvp[2], (char *) pvp,
X (int) (avp - npvp - 2) * sizeof *avp);
X# ifdef DEBUG
X if (tTd(21, 3))
X printf("-----callsubr %s\n", npvp[1]);
X# endif DEBUG
X rewrite(pvp, atoi(npvp[1]));
X }
X else
X {
X bcopy((char *) npvp, (char *) pvp,
X (int) (avp - npvp) * sizeof *avp);
X }
X# ifdef DEBUG
X if (tTd(21, 4))
X {
X printf("rewritten as:");
X printav(pvp);
X }
X# endif DEBUG
X }
X
X if (OpMode == MD_TEST || tTd(21, 2))
X {
X printf("rewrite: ruleset %2d returns:", ruleset);
X printav(pvp);
X }
X}
X/*
X** BUILDADDR -- build address from token vector.
X**
X** Parameters:
X** tv -- token vector.
X** a -- pointer to address descriptor to fill.
X** If NULL, one will be allocated.
X**
X** Returns:
X** NULL if there was an error.
X** 'a' otherwise.
X**
X** Side Effects:
X** fills in 'a'
X*/
X
XADDRESS *
Xbuildaddr(tv, a)
X register char **tv;
X register ADDRESS *a;
X{
X static char buf[MAXNAME];
X struct mailer **mp;
X register struct mailer *m;
X extern bool sameword();
X
X if (a == NULL)
X a = (ADDRESS *) xalloc(sizeof *a);
X bzero((char *) a, sizeof *a);
X
X /* figure out what net/mailer to use */
X if (**tv != CANONNET)
X {
X syserr("buildaddr: no net");
X return (NULL);
X }
X tv++;
X if (sameword(*tv, "error"))
X {
X if (**++tv == CANONHOST)
X {
X setstat(atoi(*++tv));
X tv++;
X }
X if (**tv != CANONUSER)
X syserr("buildaddr: error: no user");
X buf[0] = '\0';
X while (*++tv != NULL)
X {
X if (buf[0] != '\0')
X (void) strcat(buf, " ");
X (void) strcat(buf, *tv);
X }
X usrerr(buf);
X return (NULL);
X }
X for (mp = Mailer; (m = *mp++) != NULL; )
X {
X if (sameword(m->m_name, *tv))
X break;
X }
X if (m == NULL)
X {
X syserr("buildaddr: unknown mailer %s", *tv);
X return (NULL);
X }
X a->q_mailer = m;
X
X /* figure out what host (if any) */
X tv++;
X if (!bitnset(M_LOCAL, m->m_flags))
X {
X if (**tv++ != CANONHOST)
X {
X syserr("buildaddr: no host");
X return (NULL);
X }
X buf[0] = '\0';
X while (*tv != NULL && **tv != CANONUSER)
X (void) strcat(buf, *tv++);
X a->q_host = newstr(buf);
X }
X else
X a->q_host = NULL;
X
X /* figure out the user */
X if (**tv != CANONUSER)
X {
X syserr("buildaddr: no user");
X return (NULL);
X }
X
X /* rewrite according recipient mailer rewriting rules */
X rewrite(++tv, 2);
X if (m->m_r_rwset > 0)
X rewrite(tv, m->m_r_rwset);
X rewrite(tv, 4);
X
X /* save the result for the command line/RCPT argument */
X cataddr(tv, buf, sizeof buf);
X a->q_user = buf;
X
X return (a);
X}
X/*
X** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
X**
X** Parameters:
X** pvp -- parameter vector to rebuild.
X** buf -- buffer to build the string into.
X** sz -- size of buf.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Destroys buf.
X*/
X
Xcataddr(pvp, buf, sz)
X char **pvp;
X char *buf;
X register int sz;
X{
X bool oatomtok = FALSE;
X bool natomtok = FALSE;
X register int i;
X register char *p;
X
X if (pvp == NULL)
X {
X (void) strcpy(buf, "");
X return;
X }
X p = buf;
X sz -= 2;
X while (*pvp != NULL && (i = strlen(*pvp)) < sz)
X {
X natomtok = (toktype(**pvp) == ATM);
X if (oatomtok && natomtok)
X *p++ = SpaceSub;
X (void) strcpy(p, *pvp);
X oatomtok = natomtok;
X p += i;
X sz -= i + 1;
X pvp++;
X }
X *p = '\0';
X}
X/*
X** SAMEADDR -- Determine if two addresses are the same
X**
X** This is not just a straight comparison -- if the mailer doesn't
X** care about the host we just ignore it, etc.
X**
X** Parameters:
X** a, b -- pointers to the internal forms to compare.
X**
X** Returns:
X** TRUE -- they represent the same mailbox.
X** FALSE -- they don't.
X**
X** Side Effects:
X** none.
X*/
X
Xbool
Xsameaddr(a, b)
X register ADDRESS *a;
X register ADDRESS *b;
X{
X /* if they don't have the same mailer, forget it */
X if (a->q_mailer != b->q_mailer)
X return (FALSE);
X
X /* if the user isn't the same, we can drop out */
X if (strcmp(a->q_user, b->q_user) != 0)
X return (FALSE);
X
X /* if the mailer ignores hosts, we have succeeded! */
X if (bitnset(M_LOCAL, a->q_mailer->m_flags))
X return (TRUE);
X
X /* otherwise compare hosts (but be careful for NULL ptrs) */
X if (a->q_host == NULL || b->q_host == NULL)
X return (FALSE);
X if (strcmp(a->q_host, b->q_host) != 0)
X return (FALSE);
X
X return (TRUE);
X}
X/*
X** PRINTADDR -- print address (for debugging)
X**
X** Parameters:
X** a -- the address to print
X** follow -- follow the q_next chain.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** none.
X*/
X
X# ifdef DEBUG
X
Xprintaddr(a, follow)
X register ADDRESS *a;
X bool follow;
X{
X bool first = TRUE;
X
X while (a != NULL)
X {
X first = FALSE;
X printf("%x=", a);
X (void) fflush(stdout);
X printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr,
X a->q_mailer->m_mno, a->q_mailer->m_name, a->q_host,
X a->q_user);
X printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
X a->q_alias);
X printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
X a->q_fullname);
X
X if (!follow)
X return;
X a = a->q_next;
X }
X if (first)
X printf("[NULL]\n");
X}
X
X# endif DEBUG
X/*
X** REMOTENAME -- return the name relative to the current mailer
X**
X** Parameters:
X** name -- the name to translate.
X** m -- the mailer that we want to do rewriting relative
X** to.
X** senderaddress -- if set, uses the sender rewriting rules
X** rather than the recipient rewriting rules.
X** canonical -- if set, strip out any comment information,
X** etc.
X**
X** Returns:
X** the text string representing this address relative to
X** the receiving mailer.
X**
X** Side Effects:
X** none.
X**
X** Warnings:
X** The text string returned is tucked away locally;
X** copy it if you intend to save it.
X*/
X
Xchar *
Xremotename(name, m, senderaddress, canonical)
X char *name;
X struct mailer *m;
X bool senderaddress;
X bool canonical;
X{
X register char **pvp;
X char *fancy;
X extern char *macvalue();
X char *oldg = macvalue('g', CurEnv);
X static char buf[MAXNAME];
X char lbuf[MAXNAME];
X char pvpbuf[PSBUFSIZE];
X extern char **prescan();
X extern char *crackaddr();
X
X# ifdef DEBUG
X if (tTd(12, 1))
X printf("remotename(%s)\n", name);
X# endif DEBUG
X
X /* don't do anything if we are tagging it as special */
X if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
X return (name);
X
X /*
X ** Do a heuristic crack of this name to extract any comment info.
X ** This will leave the name as a comment and a $g macro.
X */
X
X if (canonical)
X fancy = "\001g";
X else
X fancy = crackaddr(name);
X
X /*
X ** Turn the name into canonical form.
X ** Normally this will be RFC 822 style, i.e., "user at domain".
X ** If this only resolves to "user", and the "C" flag is
X ** specified in the sending mailer, then the sender's
X ** domain will be appended.
X */
X
X pvp = prescan(name, '\0', pvpbuf);
X if (pvp == NULL)
X return (name);
X rewrite(pvp, 3);
X if (CurEnv->e_fromdomain != NULL)
X {
X /* append from domain to this address */
X register char **pxp = pvp;
X
X /* see if there is an "@domain" in the current name */
X while (*pxp != NULL && strcmp(*pxp, "@") != 0)
X pxp++;
X if (*pxp == NULL)
X {
X /* no.... append the "@domain" from the sender */
X register char **qxq = CurEnv->e_fromdomain;
X
X while ((*pxp++ = *qxq++) != NULL)
X continue;
X rewrite(pvp, 3);
X }
X }
X
X /*
X ** Do more specific rewriting.
X ** Rewrite using ruleset 1 or 2 depending on whether this is
X ** a sender address or not.
X ** Then run it through any receiving-mailer-specific rulesets.
X */
X
X if (senderaddress)
X {
X rewrite(pvp, 1);
X if (m->m_s_rwset > 0)
X rewrite(pvp, m->m_s_rwset);
X }
X else
X {
X rewrite(pvp, 2);
X if (m->m_r_rwset > 0)
X rewrite(pvp, m->m_r_rwset);
X }
X
X /*
X ** Do any final sanitation the address may require.
X ** This will normally be used to turn internal forms
X ** (e.g., user at host.LOCAL) into external form. This
X ** may be used as a default to the above rules.
X */
X
X rewrite(pvp, 4);
X
X /*
X ** Now restore the comment information we had at the beginning.
X */
X
X cataddr(pvp, lbuf, sizeof lbuf);
X define('g', lbuf, CurEnv);
X expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
X define('g', oldg, CurEnv);
X
X# ifdef DEBUG
X if (tTd(12, 1))
X printf("remotename => `%s'\n", buf);
X# endif DEBUG
X return (buf);
X}
END_OF_FILE
if test 24032 -ne `wc -c <'src/parseaddr.c'`; then
echo shar: \"'src/parseaddr.c'\" unpacked with wrong size!
fi
# end of 'src/parseaddr.c'
fi
echo shar: End of archive 7 \(of 8\).
cp /dev/null ark7isdone
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