125a99e2eSeric # include <stdio.h>
225a99e2eSeric # include <pwd.h>
325a99e2eSeric # include <signal.h>
425a99e2eSeric # include "dlvrmail.h"
525a99e2eSeric # ifdef LOG
625a99e2eSeric # include <log.h>
725a99e2eSeric # endif LOG
825a99e2eSeric 
9*ea235328Smark static char SccsId[] = "@(#)deliver.c	1.6	10/17/80";
10259cace7Seric 
1125a99e2eSeric /*
1225a99e2eSeric **  DELIVER -- Deliver a message to a particular address.
1325a99e2eSeric **
1425a99e2eSeric **	Algorithm:
1525a99e2eSeric **		Compute receiving network (i.e., mailer), host, & user.
1625a99e2eSeric **		If local, see if this is really a program name.
1725a99e2eSeric **		Build argument for the mailer.
1825a99e2eSeric **		Create pipe through edit fcn if appropriate.
1925a99e2eSeric **		Fork.
2025a99e2eSeric **			Child: call mailer
2125a99e2eSeric **		Parent: call editfcn if specified.
2225a99e2eSeric **		Wait for mailer to finish.
2325a99e2eSeric **		Interpret exit status.
2425a99e2eSeric **
2525a99e2eSeric **	Parameters:
2625a99e2eSeric **		to -- the address to deliver the message to.
2725a99e2eSeric **		editfcn -- if non-NULL, we want to call this function
2825a99e2eSeric **			to output the letter (instead of just out-
2925a99e2eSeric **			putting it raw).
3025a99e2eSeric **
3125a99e2eSeric **	Returns:
3225a99e2eSeric **		zero -- successfully delivered.
3325a99e2eSeric **		else -- some failure, see ExitStat for more info.
3425a99e2eSeric **
3525a99e2eSeric **	Side Effects:
3625a99e2eSeric **		The standard input is passed off to someone.
3725a99e2eSeric **
3825a99e2eSeric **	WARNING:
3925a99e2eSeric **		The standard input is shared amongst all children,
4025a99e2eSeric **		including the file pointer.  It is critical that the
4125a99e2eSeric **		parent waits for the child to finish before forking
4225a99e2eSeric **		another child.
4325a99e2eSeric **
4425a99e2eSeric **	Called By:
4525a99e2eSeric **		main
4625a99e2eSeric **		savemail
4725a99e2eSeric **
4825a99e2eSeric **	Files:
49a2ee7369Seric **		standard input -- must be opened to the message to
5025a99e2eSeric **			deliver.
5125a99e2eSeric */
5225a99e2eSeric 
5325a99e2eSeric deliver(to, editfcn)
5425a99e2eSeric 	addrq *to;
5525a99e2eSeric 	int (*editfcn)();
5625a99e2eSeric {
5725a99e2eSeric 	register struct mailer *m;
5825a99e2eSeric 	char *host;
5925a99e2eSeric 	char *user;
6025a99e2eSeric 	extern struct passwd *getpwnam();
6125a99e2eSeric 	char **pvp;
6225a99e2eSeric 	extern char **buildargv();
6325a99e2eSeric 	auto int st;
6425a99e2eSeric 	register int i;
6525a99e2eSeric 	register char *p;
6625a99e2eSeric 	int pid;
6725a99e2eSeric 	int pvect[2];
6825a99e2eSeric 	extern FILE *fdopen();
6925a99e2eSeric 	extern int errno;
7025a99e2eSeric 	FILE *mfile;
7125a99e2eSeric 	extern putheader();
7225a99e2eSeric 	extern pipesig();
7325a99e2eSeric 
7425a99e2eSeric 	/*
7525a99e2eSeric 	**  Compute receiving mailer, host, and to addreses.
7625a99e2eSeric 	**	Do some initialization first.  To is the to address
7725a99e2eSeric 	**	for error messages.
7825a99e2eSeric 	*/
7925a99e2eSeric 
8025a99e2eSeric 	To = to->q_paddr;
8125a99e2eSeric 	m = to->q_mailer;
8225a99e2eSeric 	user = to->q_user;
8325a99e2eSeric 	host = to->q_host;
8425a99e2eSeric 	Error = 0;
8525a99e2eSeric 	errno = 0;
8625a99e2eSeric # ifdef DEBUG
8725a99e2eSeric 	if (Debug)
8825a99e2eSeric 		printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user);
8925a99e2eSeric # endif DEBUG
9025a99e2eSeric 
9125a99e2eSeric 	/*
9225a99e2eSeric 	**  Remove quote bits from user/host.
9325a99e2eSeric 	*/
9425a99e2eSeric 
9525a99e2eSeric 	for (p = user; (*p++ &= 0177) != '\0'; )
9625a99e2eSeric 		continue;
9725a99e2eSeric 	if (host != NULL)
9825a99e2eSeric 		for (p = host; (*p++ &= 0177) != '\0'; )
9925a99e2eSeric 			continue;
10025a99e2eSeric 
10125a99e2eSeric 	/*
10225a99e2eSeric 	**  Strip quote bits from names if the mailer wants it.
10325a99e2eSeric 	*/
10425a99e2eSeric 
10525a99e2eSeric 	if (flagset(M_STRIPQ, m->m_flags))
10625a99e2eSeric 	{
10725a99e2eSeric 		stripquotes(user);
10825a99e2eSeric 		stripquotes(host);
10925a99e2eSeric 	}
11025a99e2eSeric 
11125a99e2eSeric 	/*
11225a99e2eSeric 	**  See if this user name is "special".
11325a99e2eSeric 	**	If the user is a program, diddle with the mailer spec.
11425a99e2eSeric 	**	If the user name has a slash in it, assume that this
11525a99e2eSeric 	**		is a file -- send it off without further ado.
11625a99e2eSeric 	**		Note that this means that editfcn's will not
11725a99e2eSeric 	**		be applied to the message.
11825a99e2eSeric 	*/
11925a99e2eSeric 
12025a99e2eSeric 	if (m == &Mailer[0])
12125a99e2eSeric 	{
12225a99e2eSeric 		if (*user == '|')
12325a99e2eSeric 		{
12425a99e2eSeric 			user++;
12525a99e2eSeric 			m = &Mailer[1];
12625a99e2eSeric 		}
12725a99e2eSeric 		else
12825a99e2eSeric 		{
12925a99e2eSeric 			if (index(user, '/') != NULL)
13025a99e2eSeric 			{
13125a99e2eSeric 				i = mailfile(user);
13225a99e2eSeric 				giveresponse(i, TRUE, m);
13325a99e2eSeric 				return (i);
13425a99e2eSeric 			}
13525a99e2eSeric 		}
13625a99e2eSeric 	}
13725a99e2eSeric 
13825a99e2eSeric 	/*
139243921eeSeric 	**  See if the user exists.
140243921eeSeric 	**	Strictly, this is only needed to print a pretty
14125a99e2eSeric 	**	error message.
142243921eeSeric 	**
143243921eeSeric 	**	>>>>>>>>>> This clause assumes that the local mailer
144243921eeSeric 	**	>> NOTE >> cannot do any further aliasing; that
145243921eeSeric 	**	>>>>>>>>>> function is subsumed by delivermail.
14625a99e2eSeric 	*/
14725a99e2eSeric 
14825a99e2eSeric 	if (m == &Mailer[0])
14925a99e2eSeric 	{
15025a99e2eSeric 		if (getpwnam(user) == NULL)
15125a99e2eSeric 		{
15225a99e2eSeric 			giveresponse(EX_NOUSER, TRUE, m);
15325a99e2eSeric 			return (EX_NOUSER);
15425a99e2eSeric 		}
15525a99e2eSeric 	}
15625a99e2eSeric 
15725a99e2eSeric 	/*
15825a99e2eSeric 	**  If the mailer wants a From line, insert a new editfcn.
15925a99e2eSeric 	*/
16025a99e2eSeric 
16125a99e2eSeric 	if (flagset(M_HDR, m->m_flags) && editfcn == NULL)
16225a99e2eSeric 		editfcn = putheader;
16325a99e2eSeric 
16425a99e2eSeric 	/*
16525a99e2eSeric 	**  Call the mailer.
16625a99e2eSeric 	**	The argument vector gets built, pipes through 'editfcn'
16725a99e2eSeric 	**	are created as necessary, and we fork & exec as
16825a99e2eSeric 	**	appropriate.  In the parent, we call 'editfcn'.
16925a99e2eSeric 	*/
17025a99e2eSeric 
17125a99e2eSeric 	pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr);
17225a99e2eSeric 	if (pvp == NULL)
17325a99e2eSeric 	{
17425a99e2eSeric 		usrerr("name too long");
17525a99e2eSeric 		return (-1);
17625a99e2eSeric 	}
17725a99e2eSeric 	rewind(stdin);
17825a99e2eSeric 
17925a99e2eSeric 	/* create a pipe if we will need one */
18025a99e2eSeric 	if (editfcn != NULL && pipe(pvect) < 0)
18125a99e2eSeric 	{
18225a99e2eSeric 		syserr("pipe");
18325a99e2eSeric 		return (-1);
18425a99e2eSeric 	}
185*ea235328Smark # ifdef VFORK
186*ea235328Smark 	pid = vfork();
187*ea235328Smark # else
18825a99e2eSeric 	pid = fork();
189*ea235328Smark # endif
19025a99e2eSeric 	if (pid < 0)
19125a99e2eSeric 	{
19225a99e2eSeric 		syserr("Cannot fork");
19325a99e2eSeric 		if (editfcn != NULL)
19425a99e2eSeric 		{
19525a99e2eSeric 			close(pvect[0]);
19625a99e2eSeric 			close(pvect[1]);
19725a99e2eSeric 		}
19825a99e2eSeric 		return (-1);
19925a99e2eSeric 	}
20025a99e2eSeric 	else if (pid == 0)
20125a99e2eSeric 	{
20225a99e2eSeric 		/* child -- set up input & exec mailer */
20325a99e2eSeric 		signal(SIGINT, SIG_IGN);
20425a99e2eSeric 		if (editfcn != NULL)
20525a99e2eSeric 		{
20625a99e2eSeric 			close(0);
20725a99e2eSeric 			if (dup(pvect[0]) < 0)
20825a99e2eSeric 			{
20925a99e2eSeric 				syserr("Cannot dup to zero!");
21025a99e2eSeric 				exit(EX_OSERR);
21125a99e2eSeric 			}
21225a99e2eSeric 			close(pvect[0]);
21325a99e2eSeric 			close(pvect[1]);
21425a99e2eSeric 		}
21525a99e2eSeric 		if (!flagset(M_RESTR, m->m_flags))
21625a99e2eSeric 			setuid(getuid());
21725a99e2eSeric # ifdef LOG
21825a99e2eSeric 		initlog(NULL, 0, LOG_CLOSE);
21925a99e2eSeric # endif LOG
220*ea235328Smark # ifndef VFORK
221*ea235328Smark 		/*
222*ea235328Smark 		 * We have to be careful with vfork - we can't mung up the
223*ea235328Smark 		 * memory but we don't want the mailer to inherit any extra
224*ea235328Smark 		 * open files.  Chances are the mailer won't
225*ea235328Smark 		 * care about an extra file, but then again you never know.
226*ea235328Smark 		 * Actually, we would like to close(fileno(pwf)), but it's
227*ea235328Smark 		 * declared static so we can't.  But if we fclose(pwf), which
228*ea235328Smark 		 * is what endpwent does, it closes it in the parent too and
229*ea235328Smark 		 * the next getpwnam will be slower.  If you have a weird mailer
230*ea235328Smark 		 * that chokes on the extra file you should do the endpwent().
231*ea235328Smark 		 */
23225a99e2eSeric 		endpwent();
233*ea235328Smark # endif
23425a99e2eSeric 		execv(m->m_mailer, pvp);
23525a99e2eSeric 		/* syserr fails because log is closed */
23625a99e2eSeric 		/* syserr("Cannot exec %s", m->m_mailer); */
23725a99e2eSeric 		exit(EX_UNAVAIL);
23825a99e2eSeric 	}
23925a99e2eSeric 
24025a99e2eSeric 	/* arrange to write out header message if error */
24125a99e2eSeric 	if (editfcn != NULL)
24225a99e2eSeric 	{
24325a99e2eSeric 		close(pvect[0]);
24425a99e2eSeric 		signal(SIGPIPE, pipesig);
24525a99e2eSeric 		mfile = fdopen(pvect[1], "w");
24625a99e2eSeric 		(*editfcn)(mfile);
24725a99e2eSeric 		fclose(mfile);
24825a99e2eSeric 	}
24925a99e2eSeric 
25025a99e2eSeric 	/*
25125a99e2eSeric 	**  Wait for child to die and report status.
25225a99e2eSeric 	**	We should never get fatal errors (e.g., segmentation
25325a99e2eSeric 	**	violation), so we report those specially.  For other
25425a99e2eSeric 	**	errors, we choose a status message (into statmsg),
25525a99e2eSeric 	**	and if it represents an error, we print it.
25625a99e2eSeric 	*/
25725a99e2eSeric 
25825a99e2eSeric 	while ((i = wait(&st)) > 0 && i != pid)
25925a99e2eSeric 		continue;
26025a99e2eSeric 	if (i < 0)
26125a99e2eSeric 	{
26225a99e2eSeric 		syserr("wait");
26325a99e2eSeric 		return (-1);
26425a99e2eSeric 	}
26525a99e2eSeric 	if ((st & 0377) != 0)
26625a99e2eSeric 	{
26725a99e2eSeric 		syserr("%s: stat %o", pvp[0], st);
26825a99e2eSeric 		ExitStat = EX_UNAVAIL;
26925a99e2eSeric 		return (-1);
27025a99e2eSeric 	}
27125a99e2eSeric 	i = (st >> 8) & 0377;
27225a99e2eSeric 	giveresponse(i, FALSE, m);
27325a99e2eSeric 	return (i);
27425a99e2eSeric }
27525a99e2eSeric /*
27625a99e2eSeric **  GIVERESPONSE -- Interpret an error response from a mailer
27725a99e2eSeric **
27825a99e2eSeric **	Parameters:
27925a99e2eSeric **		stat -- the status code from the mailer (high byte
28025a99e2eSeric **			only; core dumps must have been taken care of
28125a99e2eSeric **			already).
28225a99e2eSeric **		force -- if set, force an error message output, even
28325a99e2eSeric **			if the mailer seems to like to print its own
28425a99e2eSeric **			messages.
28525a99e2eSeric **		m -- the mailer descriptor for this mailer.
28625a99e2eSeric **
28725a99e2eSeric **	Returns:
28825a99e2eSeric **		none.
28925a99e2eSeric **
29025a99e2eSeric **	Side Effects:
29125a99e2eSeric **		Error may be set.
29225a99e2eSeric **		ExitStat may be set.
29325a99e2eSeric **
29425a99e2eSeric **	Called By:
29525a99e2eSeric **		deliver
29625a99e2eSeric */
29725a99e2eSeric 
29825a99e2eSeric giveresponse(stat, force, m)
29925a99e2eSeric 	int stat;
30025a99e2eSeric 	int force;
30125a99e2eSeric 	register struct mailer *m;
30225a99e2eSeric {
30325a99e2eSeric 	register char *statmsg;
30425a99e2eSeric 	extern char *SysExMsg[];
30525a99e2eSeric 	register int i;
30625a99e2eSeric 	extern int N_SysEx;
30725a99e2eSeric 
30825a99e2eSeric 	i = stat - EX__BASE;
30925a99e2eSeric 	if (i < 0 || i > N_SysEx)
31025a99e2eSeric 		statmsg = NULL;
31125a99e2eSeric 	else
31225a99e2eSeric 		statmsg = SysExMsg[i];
31325a99e2eSeric 	if (stat == 0)
31425a99e2eSeric 		statmsg = "ok";
31525a99e2eSeric 	else
31625a99e2eSeric 	{
31725a99e2eSeric 		Error++;
31825a99e2eSeric 		if (statmsg == NULL && m->m_badstat != 0)
31925a99e2eSeric 		{
32025a99e2eSeric 			stat = m->m_badstat;
32125a99e2eSeric 			i = stat - EX__BASE;
32225a99e2eSeric # ifdef DEBUG
32325a99e2eSeric 			if (i < 0 || i >= N_SysEx)
32425a99e2eSeric 				syserr("Bad m_badstat %d", stat);
32525a99e2eSeric 			else
32625a99e2eSeric # endif DEBUG
32725a99e2eSeric 			statmsg = SysExMsg[i];
32825a99e2eSeric 		}
32925a99e2eSeric 		if (statmsg == NULL)
33025a99e2eSeric 			usrerr("unknown mailer response %d", stat);
33125a99e2eSeric 		else if (force || !flagset(M_QUIET, m->m_flags))
33225a99e2eSeric 			usrerr("%s", statmsg);
33325a99e2eSeric 	}
33425a99e2eSeric 
33525a99e2eSeric 	/*
33625a99e2eSeric 	**  Final cleanup.
33725a99e2eSeric 	**	Log a record of the transaction.  Compute the new
33825a99e2eSeric 	**	ExitStat -- if we already had an error, stick with
33925a99e2eSeric 	**	that.
34025a99e2eSeric 	*/
34125a99e2eSeric 
34225a99e2eSeric # ifdef LOG
34325a99e2eSeric 	if (statmsg == NULL)
34425a99e2eSeric 		logmsg(LOG_INFO, "%s->%s: error %d", From.q_paddr, To, stat);
34525a99e2eSeric 	else
34625a99e2eSeric 		logmsg(LOG_INFO, "%s->%s: %s", From.q_paddr, To, statmsg);
34725a99e2eSeric # endif LOG
348243921eeSeric 	setstat(stat);
34925a99e2eSeric 	return (stat);
35025a99e2eSeric }
35125a99e2eSeric /*
35225a99e2eSeric **  PUTHEADER -- insert the From header into some mail
35325a99e2eSeric **
35425a99e2eSeric **	For mailers such as 'msgs' that want the header inserted
35525a99e2eSeric **	into the mail, this edit filter inserts the From line and
35625a99e2eSeric **	then passes the rest of the message through.
35725a99e2eSeric **
35825a99e2eSeric **	Parameters:
35925a99e2eSeric **		fp -- the file pointer for the output.
36025a99e2eSeric **
36125a99e2eSeric **	Returns:
36225a99e2eSeric **		none
36325a99e2eSeric **
36425a99e2eSeric **	Side Effects:
36525a99e2eSeric **		Puts a "From" line in UNIX format, and then
36625a99e2eSeric **			outputs the rest of the message.
36725a99e2eSeric **
36825a99e2eSeric **	Called By:
36925a99e2eSeric **		deliver
37025a99e2eSeric */
37125a99e2eSeric 
37225a99e2eSeric putheader(fp)
37325a99e2eSeric 	register FILE *fp;
37425a99e2eSeric {
37525a99e2eSeric 	char buf[MAXLINE + 1];
37625a99e2eSeric 	long tim;
37725a99e2eSeric 	extern char *ctime();
37825a99e2eSeric 
37925a99e2eSeric 	time(&tim);
38025a99e2eSeric 	fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim));
38125a99e2eSeric 	while (fgets(buf, sizeof buf, stdin) != NULL && !ferror(fp))
38225a99e2eSeric 		fputs(buf, fp);
38325a99e2eSeric 	if (ferror(fp))
38425a99e2eSeric 	{
38525a99e2eSeric 		syserr("putheader: write error");
38625a99e2eSeric 		setstat(EX_IOERR);
38725a99e2eSeric 	}
38825a99e2eSeric }
38925a99e2eSeric /*
39025a99e2eSeric **  PIPESIG -- Handle broken pipe signals
39125a99e2eSeric **
39225a99e2eSeric **	This just logs an error.
39325a99e2eSeric **
39425a99e2eSeric **	Parameters:
39525a99e2eSeric **		none
39625a99e2eSeric **
39725a99e2eSeric **	Returns:
39825a99e2eSeric **		none
39925a99e2eSeric **
40025a99e2eSeric **	Side Effects:
40125a99e2eSeric **		logs an error message.
40225a99e2eSeric */
40325a99e2eSeric 
40425a99e2eSeric pipesig()
40525a99e2eSeric {
40625a99e2eSeric 	syserr("Broken pipe");
40725a99e2eSeric }
40825a99e2eSeric /*
40925a99e2eSeric **  SENDTO -- Designate a send list.
41025a99e2eSeric **
41125a99e2eSeric **	The parameter is a comma-separated list of people to send to.
41225a99e2eSeric **	This routine arranges to send to all of them.
41325a99e2eSeric **
41425a99e2eSeric **	Parameters:
41525a99e2eSeric **		list -- the send list.
41625a99e2eSeric **		copyf -- the copy flag; passed to parse.
41725a99e2eSeric **
41825a99e2eSeric **	Returns:
41925a99e2eSeric **		none
42025a99e2eSeric **
42125a99e2eSeric **	Side Effects:
42225a99e2eSeric **		none.
42325a99e2eSeric **
42425a99e2eSeric **	Called By:
42525a99e2eSeric **		main
42625a99e2eSeric **		alias
42725a99e2eSeric */
42825a99e2eSeric 
42925a99e2eSeric sendto(list, copyf)
43025a99e2eSeric 	char *list;
43125a99e2eSeric 	int copyf;
43225a99e2eSeric {
43325a99e2eSeric 	register char *p;
43425a99e2eSeric 	register char *q;
43525a99e2eSeric 	register char c;
43625a99e2eSeric 	addrq *a;
43725a99e2eSeric 	extern addrq *parse();
43825a99e2eSeric 	bool more;
43925a99e2eSeric 
44025a99e2eSeric 	/* more keeps track of what the previous delimiter was */
44125a99e2eSeric 	more = TRUE;
44225a99e2eSeric 	for (p = list; more; )
44325a99e2eSeric 	{
44425a99e2eSeric 		/* find the end of this address */
44525a99e2eSeric 		q = p;
44625a99e2eSeric 		while ((c = *p++) != '\0' && c != ',' && c != '\n')
44725a99e2eSeric 			continue;
44825a99e2eSeric 		more = c != '\0';
44925a99e2eSeric 		*--p = '\0';
45025a99e2eSeric 		if (more)
45125a99e2eSeric 			p++;
45225a99e2eSeric 
45325a99e2eSeric 		/* parse the address */
45425a99e2eSeric 		if ((a = parse(q, (addrq *) NULL, copyf)) == NULL)
45525a99e2eSeric 			continue;
45625a99e2eSeric 
45725a99e2eSeric 		/* arrange to send to this person */
45825a99e2eSeric 		recipient(a, &SendQ);
45925a99e2eSeric 	}
46025a99e2eSeric 	To = NULL;
46125a99e2eSeric }
46225a99e2eSeric /*
46325a99e2eSeric **  RECIPIENT -- Designate a message recipient
46425a99e2eSeric **
46525a99e2eSeric **	Saves the named person for future mailing.
46625a99e2eSeric **
46725a99e2eSeric **	Designates a person as a recipient.  This routine
46825a99e2eSeric **	does the initial parsing, and checks to see if
46925a99e2eSeric **	this person has already received the mail.
47025a99e2eSeric **	It also supresses local network names and turns them into
47125a99e2eSeric **	local names.
47225a99e2eSeric **
47325a99e2eSeric **	Parameters:
47425a99e2eSeric **		a -- the (preparsed) address header for the recipient.
47525a99e2eSeric **		targetq -- the queue to add the name to.
47625a99e2eSeric **
47725a99e2eSeric **	Returns:
47825a99e2eSeric **		none.
47925a99e2eSeric **
48025a99e2eSeric **	Side Effects:
48125a99e2eSeric **		none.
48225a99e2eSeric **
48325a99e2eSeric **	Called By:
48425a99e2eSeric **		sendto
48525a99e2eSeric **		main
48625a99e2eSeric */
48725a99e2eSeric 
48825a99e2eSeric recipient(a, targetq)
48925a99e2eSeric 	register addrq *a;
49025a99e2eSeric 	addrq *targetq;
49125a99e2eSeric {
49225a99e2eSeric 	register addrq *q;
49325a99e2eSeric 	register struct mailer *m;
49425a99e2eSeric 	register char **pvp;
49525a99e2eSeric 	extern char *xalloc();
49625a99e2eSeric 	extern bool forward();
49725a99e2eSeric 	extern int errno;
49825a99e2eSeric 	extern bool sameaddr();
49925a99e2eSeric 
50025a99e2eSeric 	To = a->q_paddr;
50125a99e2eSeric 	m = a->q_mailer;
50225a99e2eSeric 	errno = 0;
50325a99e2eSeric # ifdef DEBUG
50425a99e2eSeric 	if (Debug)
50525a99e2eSeric 		printf("recipient(%s)\n", To);
50625a99e2eSeric # endif DEBUG
50725a99e2eSeric 
50825a99e2eSeric 	/*
50925a99e2eSeric 	**  Don't go to the net if already on the target host.
51025a99e2eSeric 	**	This is important on the berkeley network, since
51125a99e2eSeric 	**	it get confused if we ask to send to ourselves.
51225a99e2eSeric 	**	For nets like the ARPANET, we probably will have
51325a99e2eSeric 	**	the local list set to NULL to simplify testing.
51425a99e2eSeric 	**	The canonical representation of the name is also set
51525a99e2eSeric 	**	to be just the local name so the duplicate letter
51625a99e2eSeric 	**	suppression algorithm will work.
51725a99e2eSeric 	*/
51825a99e2eSeric 
51925a99e2eSeric 	if ((pvp = m->m_local) != NULL)
52025a99e2eSeric 	{
52125a99e2eSeric 		while (*pvp != NULL)
52225a99e2eSeric 		{
52325a99e2eSeric 			if (strcmp(*pvp++, a->q_host) == 0)
52425a99e2eSeric 			{
52525a99e2eSeric 				a->q_mailer = m = &Mailer[0];
52625a99e2eSeric 				break;
52725a99e2eSeric 			}
52825a99e2eSeric 		}
52925a99e2eSeric 	}
53025a99e2eSeric 
53125a99e2eSeric 	/*
53225a99e2eSeric 	**  Look up this person in the recipient list.  If they
53325a99e2eSeric 	**  are there already, return, otherwise continue.
53425a99e2eSeric 	*/
53525a99e2eSeric 
53625a99e2eSeric 	if (!ForceMail)
53725a99e2eSeric 	{
53825a99e2eSeric 		for (q = &SendQ; (q = nxtinq(q)) != NULL; )
53925a99e2eSeric 			if (sameaddr(q, a, FALSE))
54025a99e2eSeric 			{
54125a99e2eSeric # ifdef DEBUG
54225a99e2eSeric 				if (Debug)
54325a99e2eSeric 					printf("(%s in SendQ)\n", a->q_paddr);
54425a99e2eSeric # endif DEBUG
54525a99e2eSeric 				return;
54625a99e2eSeric 			}
54725a99e2eSeric 		for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
54825a99e2eSeric 			if (sameaddr(q, a, FALSE))
54925a99e2eSeric 			{
55025a99e2eSeric # ifdef DEBUG
55125a99e2eSeric 				if (Debug)
55225a99e2eSeric 					printf("(%s in AliasQ)\n", a->q_paddr);
55325a99e2eSeric # endif DEBUG
55425a99e2eSeric 				return;
55525a99e2eSeric 			}
55625a99e2eSeric 	}
55725a99e2eSeric 
55825a99e2eSeric 	/*
55925a99e2eSeric 	**  See if the user wants hir mail forwarded.
56025a99e2eSeric 	**	`Forward' must do the forwarding recursively.
56125a99e2eSeric 	*/
56225a99e2eSeric 
56325a99e2eSeric 	if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
56425a99e2eSeric 		return;
56525a99e2eSeric 
56625a99e2eSeric 	/*
56725a99e2eSeric 	**  Put the user onto the target queue.
56825a99e2eSeric 	*/
56925a99e2eSeric 
57025a99e2eSeric 	if (targetq != NULL)
57125a99e2eSeric 	{
57225a99e2eSeric 		putonq(a, targetq);
57325a99e2eSeric 	}
57425a99e2eSeric 
57525a99e2eSeric 	return;
57625a99e2eSeric }
57725a99e2eSeric /*
57825a99e2eSeric **  BUILDARGV -- Build an argument vector for a mail server.
57925a99e2eSeric **
58025a99e2eSeric **	Using a template defined in config.c, an argv is built.
58125a99e2eSeric **	The format of the template is already a vector.  The
58225a99e2eSeric **	items of this vector are copied, unless a dollar sign
58325a99e2eSeric **	is encountered.  In this case, the next character
58425a99e2eSeric **	specifies something else to copy in.  These can be
58525a99e2eSeric **		$f	The from address.
58625a99e2eSeric **		$h	The host.
58725a99e2eSeric **		$u	The user.
58825a99e2eSeric **		$c	The hop count.
58925a99e2eSeric **	The vector is built in a local buffer.  A pointer to
59025a99e2eSeric **	the static argv is returned.
59125a99e2eSeric **
59225a99e2eSeric **	Parameters:
59325a99e2eSeric **		tmplt -- a template for an argument vector.
59425a99e2eSeric **		flags -- the flags for this server.
59525a99e2eSeric **		host -- the host name to send to.
59625a99e2eSeric **		user -- the user name to send to.
59725a99e2eSeric **		from -- the person this mail is from.
59825a99e2eSeric **
59925a99e2eSeric **	Returns:
60025a99e2eSeric **		A pointer to an argv.
60125a99e2eSeric **
60225a99e2eSeric **	Side Effects:
60325a99e2eSeric **		none
60425a99e2eSeric **
60525a99e2eSeric **	WARNING:
60625a99e2eSeric **		Since the argv is staticly allocated, any subsequent
60725a99e2eSeric **		calls will clobber the old argv.
60825a99e2eSeric **
60925a99e2eSeric **	Called By:
61025a99e2eSeric **		deliver
61125a99e2eSeric */
61225a99e2eSeric 
61325a99e2eSeric char **
61425a99e2eSeric buildargv(tmplt, flags, host, user, from)
61525a99e2eSeric 	char **tmplt;
61625a99e2eSeric 	int flags;
61725a99e2eSeric 	char *host;
61825a99e2eSeric 	char *user;
61925a99e2eSeric 	char *from;
62025a99e2eSeric {
62125a99e2eSeric 	register char *p;
62225a99e2eSeric 	register char *q;
62325a99e2eSeric 	static char *pv[MAXPV+1];
62425a99e2eSeric 	char **pvp;
62525a99e2eSeric 	char **mvp;
62625a99e2eSeric 	static char buf[512];
62725a99e2eSeric 	register char *bp;
62825a99e2eSeric 	char pbuf[30];
62925a99e2eSeric 
63025a99e2eSeric 	/*
63125a99e2eSeric 	**  Do initial argv setup.
63225a99e2eSeric 	**	Insert the mailer name.  Notice that $x expansion is
63325a99e2eSeric 	**	NOT done on the mailer name.  Then, if the mailer has
63425a99e2eSeric 	**	a picky -f flag, we insert it as appropriate.  This
63525a99e2eSeric 	**	code does not check for 'pv' overflow; this places a
63625a99e2eSeric 	**	manifest lower limit of 4 for MAXPV.
63725a99e2eSeric 	*/
63825a99e2eSeric 
63925a99e2eSeric 	pvp = pv;
64025a99e2eSeric 	bp = buf;
64125a99e2eSeric 
64225a99e2eSeric 	*pvp++ = tmplt[0];
64325a99e2eSeric 
64425a99e2eSeric 	/* insert -f or -r flag as appropriate */
64525a99e2eSeric 	if (flagset(M_FOPT|M_ROPT, flags) && FromFlag)
64625a99e2eSeric 	{
64725a99e2eSeric 		if (flagset(M_FOPT, flags))
64825a99e2eSeric 			*pvp++ = "-f";
64925a99e2eSeric 		else
65025a99e2eSeric 			*pvp++ = "-r";
65125a99e2eSeric 		*pvp++ = From.q_paddr;
65225a99e2eSeric 	}
65325a99e2eSeric 
65425a99e2eSeric 	/*
65525a99e2eSeric 	**  Build the rest of argv.
65625a99e2eSeric 	**	For each prototype parameter, the prototype is
65725a99e2eSeric 	**	scanned character at a time.  If a dollar-sign is
65825a99e2eSeric 	**	found, 'q' is set to the appropriate expansion,
65925a99e2eSeric 	**	otherwise it is null.  Then either the string
66025a99e2eSeric 	**	pointed to by q, or the original character, is
66125a99e2eSeric 	**	interpolated into the buffer.  Buffer overflow is
66225a99e2eSeric 	**	checked.
66325a99e2eSeric 	*/
66425a99e2eSeric 
66525a99e2eSeric 	for (mvp = tmplt; (p = *++mvp) != NULL; )
66625a99e2eSeric 	{
66725a99e2eSeric 		if (pvp >= &pv[MAXPV])
66825a99e2eSeric 		{
66925a99e2eSeric 			syserr("Too many parameters to %s", pv[0]);
67025a99e2eSeric 			return (NULL);
67125a99e2eSeric 		}
67225a99e2eSeric 		*pvp++ = bp;
67325a99e2eSeric 		for (; *p != '\0'; p++)
67425a99e2eSeric 		{
67525a99e2eSeric 			/* q will be the interpolated quantity */
67625a99e2eSeric 			q = NULL;
67725a99e2eSeric 			if (*p == '$')
67825a99e2eSeric 			{
67925a99e2eSeric 				switch (*++p)
68025a99e2eSeric 				{
68125a99e2eSeric 				  case 'f':	/* from person */
68225a99e2eSeric 					q = from;
68325a99e2eSeric 					break;
68425a99e2eSeric 
68525a99e2eSeric 				  case 'u':	/* user */
68625a99e2eSeric 					q = user;
68725a99e2eSeric 					break;
68825a99e2eSeric 
68925a99e2eSeric 				  case 'h':	/* host */
69025a99e2eSeric 					q = host;
69125a99e2eSeric 					break;
69225a99e2eSeric 
69325a99e2eSeric 				  case 'c':	/* hop count */
69425a99e2eSeric 					sprintf(pbuf, "%d", HopCount);
69525a99e2eSeric 					q = pbuf;
69625a99e2eSeric 					break;
69725a99e2eSeric 				}
69825a99e2eSeric 			}
69925a99e2eSeric 
70025a99e2eSeric 			/*
70125a99e2eSeric 			**  Interpolate q or output one character
70225a99e2eSeric 			**	Strip quote bits as we proceed.....
70325a99e2eSeric 			*/
70425a99e2eSeric 
70525a99e2eSeric 			if (q != NULL)
70625a99e2eSeric 			{
70725a99e2eSeric 				while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
70825a99e2eSeric 					continue;
70925a99e2eSeric 				bp--;
71025a99e2eSeric 			}
71125a99e2eSeric 			else if (bp < &buf[sizeof buf - 1])
71225a99e2eSeric 				*bp++ = *p;
71325a99e2eSeric 		}
71425a99e2eSeric 		*bp++ = '\0';
71525a99e2eSeric 		if (bp >= &buf[sizeof buf - 1])
71625a99e2eSeric 			return (NULL);
71725a99e2eSeric 	}
71825a99e2eSeric 	*pvp = NULL;
71925a99e2eSeric 
72025a99e2eSeric # ifdef DEBUG
72125a99e2eSeric 	if (Debug)
72225a99e2eSeric 	{
72325a99e2eSeric 		printf("Interpolated argv is:\n");
72425a99e2eSeric 		for (mvp = pv; *mvp != NULL; mvp++)
72525a99e2eSeric 			printf("\t%s\n", *mvp);
72625a99e2eSeric 	}
72725a99e2eSeric # endif DEBUG
72825a99e2eSeric 
72925a99e2eSeric 	return (pv);
73025a99e2eSeric }
73125a99e2eSeric /*
73225a99e2eSeric **  MAILFILE -- Send a message to a file.
73325a99e2eSeric **
73425a99e2eSeric **	Parameters:
73525a99e2eSeric **		filename -- the name of the file to send to.
73625a99e2eSeric **
73725a99e2eSeric **	Returns:
73825a99e2eSeric **		The exit code associated with the operation.
73925a99e2eSeric **
74025a99e2eSeric **	Side Effects:
74125a99e2eSeric **		none.
74225a99e2eSeric **
74325a99e2eSeric **	Called By:
74425a99e2eSeric **		deliver
74525a99e2eSeric */
74625a99e2eSeric 
74725a99e2eSeric mailfile(filename)
74825a99e2eSeric 	char *filename;
74925a99e2eSeric {
75025a99e2eSeric 	char buf[MAXLINE];
75125a99e2eSeric 	register FILE *f;
75225a99e2eSeric 	auto long tim;
75325a99e2eSeric 	extern char *ctime();
75425a99e2eSeric 
75525a99e2eSeric 	f = fopen(filename, "a");
75625a99e2eSeric 	if (f == NULL)
75725a99e2eSeric 		return (EX_CANTCREAT);
75825a99e2eSeric 
75925a99e2eSeric 	/* output the timestamp */
76025a99e2eSeric 	time(&tim);
76125a99e2eSeric 	fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
76225a99e2eSeric 	rewind(stdin);
76325a99e2eSeric 	while (fgets(buf, sizeof buf, stdin) != NULL)
76425a99e2eSeric 	{
76525a99e2eSeric 		fputs(buf, f);
76625a99e2eSeric 		if (ferror(f))
76725a99e2eSeric 		{
76825a99e2eSeric 			fclose(f);
76925a99e2eSeric 			return (EX_IOERR);
77025a99e2eSeric 		}
77125a99e2eSeric 	}
77225a99e2eSeric 	fputs("\n", f);
77325a99e2eSeric 	fclose(f);
77425a99e2eSeric 	return (EX_OK);
77525a99e2eSeric }
776