1294Seric # include <stdio.h>
2294Seric # include <pwd.h>
3294Seric # include <signal.h>
42898Seric # include <ctype.h>
52968Seric # include "postbox.h"
6294Seric # ifdef LOG
72774Seric # include <syslog.h>
8294Seric # endif LOG
9294Seric 
10*3044Seric static char SccsId[] = "@(#)deliver.c	3.3	03/07/81";
11405Seric 
12294Seric /*
13294Seric **  DELIVER -- Deliver a message to a particular address.
14294Seric **
15294Seric **	Algorithm:
16294Seric **		Compute receiving network (i.e., mailer), host, & user.
17294Seric **		If local, see if this is really a program name.
18294Seric **		Build argument for the mailer.
19294Seric **		Create pipe through edit fcn if appropriate.
20294Seric **		Fork.
21294Seric **			Child: call mailer
22294Seric **		Parent: call editfcn if specified.
23294Seric **		Wait for mailer to finish.
24294Seric **		Interpret exit status.
25294Seric **
26294Seric **	Parameters:
27294Seric **		to -- the address to deliver the message to.
28294Seric **		editfcn -- if non-NULL, we want to call this function
29294Seric **			to output the letter (instead of just out-
30294Seric **			putting it raw).
31294Seric **
32294Seric **	Returns:
33294Seric **		zero -- successfully delivered.
34294Seric **		else -- some failure, see ExitStat for more info.
35294Seric **
36294Seric **	Side Effects:
37294Seric **		The standard input is passed off to someone.
38294Seric **
39294Seric **	WARNING:
40294Seric **		The standard input is shared amongst all children,
41294Seric **		including the file pointer.  It is critical that the
42294Seric **		parent waits for the child to finish before forking
43294Seric **		another child.
44294Seric **
45294Seric **	Called By:
46294Seric **		main
47294Seric **		savemail
48294Seric **
49294Seric **	Files:
50570Seric **		standard input -- must be opened to the message to
51294Seric **			deliver.
52294Seric */
53294Seric 
54294Seric deliver(to, editfcn)
552968Seric 	ADDRESS *to;
56294Seric 	int (*editfcn)();
57294Seric {
58294Seric 	register struct mailer *m;
59294Seric 	char *host;
60294Seric 	char *user;
61294Seric 	extern struct passwd *getpwnam();
62294Seric 	char **pvp;
63294Seric 	extern char **buildargv();
64294Seric 	auto int st;
65294Seric 	register int i;
66294Seric 	register char *p;
67294Seric 	int pid;
68294Seric 	int pvect[2];
69294Seric 	extern FILE *fdopen();
70294Seric 	extern int errno;
71294Seric 	FILE *mfile;
722898Seric 	extern putmessage();
73294Seric 	extern pipesig();
741828Seric 	extern char *index();
752968Seric 	extern bool checkcompat();
76294Seric 
77294Seric 	/*
78294Seric 	**  Compute receiving mailer, host, and to addreses.
79294Seric 	**	Do some initialization first.  To is the to address
80294Seric 	**	for error messages.
81294Seric 	*/
82294Seric 
83294Seric 	To = to->q_paddr;
84294Seric 	m = to->q_mailer;
85294Seric 	user = to->q_user;
86294Seric 	host = to->q_host;
871518Seric 	Errors = 0;
88294Seric 	errno = 0;
89294Seric # ifdef DEBUG
90294Seric 	if (Debug)
91294Seric 		printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user);
92294Seric # endif DEBUG
93294Seric 
94294Seric 	/*
952968Seric 	**  Check to see that these people are allowed to talk to each other.
962968Seric 	*/
972968Seric 
982968Seric 	if (!checkcompat(to))
992968Seric 		return(giveresponse(EX_UNAVAILABLE, TRUE, m));
1002968Seric 
1012968Seric 	/*
102294Seric 	**  Remove quote bits from user/host.
103294Seric 	*/
104294Seric 
105294Seric 	for (p = user; (*p++ &= 0177) != '\0'; )
106294Seric 		continue;
107294Seric 	if (host != NULL)
108294Seric 		for (p = host; (*p++ &= 0177) != '\0'; )
109294Seric 			continue;
110294Seric 
111294Seric 	/*
112294Seric 	**  Strip quote bits from names if the mailer wants it.
113294Seric 	*/
114294Seric 
1152968Seric 	if (bitset(M_STRIPQ, m->m_flags))
116294Seric 	{
117294Seric 		stripquotes(user);
118294Seric 		stripquotes(host);
119294Seric 	}
120294Seric 
121294Seric 	/*
122294Seric 	**  See if this user name is "special".
123294Seric 	**	If the user is a program, diddle with the mailer spec.
124294Seric 	**	If the user name has a slash in it, assume that this
125294Seric 	**		is a file -- send it off without further ado.
126294Seric 	**		Note that this means that editfcn's will not
127294Seric 	**		be applied to the message.
128294Seric 	*/
129294Seric 
130294Seric 	if (m == &Mailer[0])
131294Seric 	{
132294Seric 		if (*user == '|')
133294Seric 		{
134294Seric 			user++;
135294Seric 			m = &Mailer[1];
136294Seric 		}
137294Seric 		else
138294Seric 		{
139294Seric 			if (index(user, '/') != NULL)
140294Seric 			{
141294Seric 				i = mailfile(user);
142294Seric 				giveresponse(i, TRUE, m);
143294Seric 				return (i);
144294Seric 			}
145294Seric 		}
146294Seric 	}
147294Seric 
148294Seric 	/*
1491389Seric 	**  See if the user exists.
1501389Seric 	**	Strictly, this is only needed to print a pretty
1511389Seric 	**	error message.
1521389Seric 	**
1531389Seric 	**	>>>>>>>>>> This clause assumes that the local mailer
1541389Seric 	**	>> NOTE >> cannot do any further aliasing; that
1552968Seric 	**	>>>>>>>>>> function is subsumed by postbox.
156294Seric 	*/
157294Seric 
158294Seric 	if (m == &Mailer[0])
159294Seric 	{
160294Seric 		if (getpwnam(user) == NULL)
161294Seric 		{
162294Seric 			giveresponse(EX_NOUSER, TRUE, m);
163294Seric 			return (EX_NOUSER);
164294Seric 		}
165294Seric 	}
166294Seric 
167294Seric 	/*
168294Seric 	**  Call the mailer.
1692898Seric 	**	The argument vector gets built, pipes
170294Seric 	**	are created as necessary, and we fork & exec as
1712898Seric 	**	appropriate.
172294Seric 	*/
173294Seric 
1742898Seric 	pvp = buildargv(m, host, user, From.q_paddr);
175294Seric 	if (pvp == NULL)
176294Seric 	{
177294Seric 		usrerr("name too long");
178294Seric 		return (-1);
179294Seric 	}
180294Seric 	rewind(stdin);
181294Seric 
1822898Seric 	/* create a pipe to shove the mail through */
1832898Seric 	if (pipe(pvect) < 0)
184294Seric 	{
185294Seric 		syserr("pipe");
186294Seric 		return (-1);
187294Seric 	}
1881504Smark # ifdef VFORK
1891504Smark 	pid = vfork();
1901504Smark # else
191294Seric 	pid = fork();
1921504Smark # endif
193294Seric 	if (pid < 0)
194294Seric 	{
195294Seric 		syserr("Cannot fork");
1962898Seric 		close(pvect[0]);
1972898Seric 		close(pvect[1]);
198294Seric 		return (-1);
199294Seric 	}
200294Seric 	else if (pid == 0)
201294Seric 	{
202294Seric 		/* child -- set up input & exec mailer */
2031621Seric 		/* make diagnostic output be standard output */
2041621Seric 		close(2);
2051621Seric 		dup(1);
206294Seric 		signal(SIGINT, SIG_IGN);
2072898Seric 		close(0);
2082898Seric 		if (dup(pvect[0]) < 0)
209294Seric 		{
2102898Seric 			syserr("Cannot dup to zero!");
2112898Seric 			_exit(EX_OSERR);
212294Seric 		}
2132898Seric 		close(pvect[0]);
2142898Seric 		close(pvect[1]);
2152968Seric 		if (!bitset(M_RESTR, m->m_flags))
216294Seric 			setuid(getuid());
2172774Seric # ifndef VFORK
2182774Seric 		/*
2192774Seric 		**  We have to be careful with vfork - we can't mung up the
2202774Seric 		**  memory but we don't want the mailer to inherit any extra
2212774Seric 		**  open files.  Chances are the mailer won't
2222774Seric 		**  care about an extra file, but then again you never know.
2232774Seric 		**  Actually, we would like to close(fileno(pwf)), but it's
2242774Seric 		**  declared static so we can't.  But if we fclose(pwf), which
2252774Seric 		**  is what endpwent does, it closes it in the parent too and
2262774Seric 		**  the next getpwnam will be slower.  If you have a weird
2272774Seric 		**  mailer that chokes on the extra file you should do the
2282774Seric 		**  endpwent().
2292774Seric 		**
2302774Seric 		**  Similar comments apply to log.  However, openlog is
2312774Seric 		**  clever enough to set the FIOCLEX mode on the file,
2322774Seric 		**  so it will be closed automatically on the exec.
2332774Seric 		*/
2342774Seric 
2352774Seric 		endpwent();
236294Seric # ifdef LOG
2372089Seric 		closelog();
238294Seric # endif LOG
2392774Seric # endif VFORK
240294Seric 		execv(m->m_mailer, pvp);
241294Seric 		/* syserr fails because log is closed */
242294Seric 		/* syserr("Cannot exec %s", m->m_mailer); */
2432343Seric 		printf("Cannot exec %s\n", m->m_mailer);
2442343Seric 		fflush(stdout);
2451619Seric 		_exit(EX_UNAVAILABLE);
246294Seric 	}
247294Seric 
2482898Seric 	/* write out message to mailer */
2492898Seric 	close(pvect[0]);
2502898Seric 	signal(SIGPIPE, pipesig);
2512898Seric 	mfile = fdopen(pvect[1], "w");
2522898Seric 	if (editfcn == NULL)
2532898Seric 		editfcn = putmessage;
2542898Seric 	(*editfcn)(mfile, m);
2552898Seric 	fclose(mfile);
256294Seric 
257294Seric 	/*
258294Seric 	**  Wait for child to die and report status.
259294Seric 	**	We should never get fatal errors (e.g., segmentation
260294Seric 	**	violation), so we report those specially.  For other
261294Seric 	**	errors, we choose a status message (into statmsg),
262294Seric 	**	and if it represents an error, we print it.
263294Seric 	*/
264294Seric 
265294Seric 	while ((i = wait(&st)) > 0 && i != pid)
266294Seric 		continue;
267294Seric 	if (i < 0)
268294Seric 	{
269294Seric 		syserr("wait");
270294Seric 		return (-1);
271294Seric 	}
272294Seric 	if ((st & 0377) != 0)
273294Seric 	{
274294Seric 		syserr("%s: stat %o", pvp[0], st);
2751597Seric 		ExitStat = EX_UNAVAILABLE;
276294Seric 		return (-1);
277294Seric 	}
278294Seric 	i = (st >> 8) & 0377;
2792343Seric 	giveresponse(i, TRUE, m);
280294Seric 	return (i);
281294Seric }
282294Seric /*
283294Seric **  GIVERESPONSE -- Interpret an error response from a mailer
284294Seric **
285294Seric **	Parameters:
286294Seric **		stat -- the status code from the mailer (high byte
287294Seric **			only; core dumps must have been taken care of
288294Seric **			already).
289294Seric **		force -- if set, force an error message output, even
290294Seric **			if the mailer seems to like to print its own
291294Seric **			messages.
292294Seric **		m -- the mailer descriptor for this mailer.
293294Seric **
294294Seric **	Returns:
2952968Seric **		stat.
296294Seric **
297294Seric **	Side Effects:
2981518Seric **		Errors may be incremented.
299294Seric **		ExitStat may be set.
300294Seric **
301294Seric **	Called By:
302294Seric **		deliver
303294Seric */
304294Seric 
305294Seric giveresponse(stat, force, m)
306294Seric 	int stat;
307294Seric 	int force;
308294Seric 	register struct mailer *m;
309294Seric {
310294Seric 	register char *statmsg;
311294Seric 	extern char *SysExMsg[];
312294Seric 	register int i;
313294Seric 	extern int N_SysEx;
3141624Seric 	extern long MsgSize;
3151624Seric 	char buf[30];
316*3044Seric 	extern char *sprintf();
317294Seric 
318294Seric 	i = stat - EX__BASE;
319294Seric 	if (i < 0 || i > N_SysEx)
320294Seric 		statmsg = NULL;
321294Seric 	else
322294Seric 		statmsg = SysExMsg[i];
323294Seric 	if (stat == 0)
324294Seric 		statmsg = "ok";
325294Seric 	else
326294Seric 	{
3271518Seric 		Errors++;
328294Seric 		if (statmsg == NULL && m->m_badstat != 0)
329294Seric 		{
330294Seric 			stat = m->m_badstat;
331294Seric 			i = stat - EX__BASE;
332294Seric # ifdef DEBUG
333294Seric 			if (i < 0 || i >= N_SysEx)
334294Seric 				syserr("Bad m_badstat %d", stat);
335294Seric 			else
336294Seric # endif DEBUG
337294Seric 			statmsg = SysExMsg[i];
338294Seric 		}
339294Seric 		if (statmsg == NULL)
340294Seric 			usrerr("unknown mailer response %d", stat);
3412968Seric 		else if (force || !bitset(M_QUIET, m->m_flags))
342294Seric 			usrerr("%s", statmsg);
343294Seric 	}
344294Seric 
345294Seric 	/*
346294Seric 	**  Final cleanup.
347294Seric 	**	Log a record of the transaction.  Compute the new
348294Seric 	**	ExitStat -- if we already had an error, stick with
349294Seric 	**	that.
350294Seric 	*/
351294Seric 
3521624Seric 	if (statmsg == NULL)
3531624Seric 	{
3541624Seric 		sprintf(buf, "error %d", stat);
3551624Seric 		statmsg = buf;
3561624Seric 	}
3571624Seric 
358294Seric # ifdef LOG
3592774Seric 	syslog(LOG_INFO, "%s->%s: %ld: %s", From.q_paddr, To, MsgSize, statmsg);
360294Seric # endif LOG
3611389Seric 	setstat(stat);
362294Seric 	return (stat);
363294Seric }
364294Seric /*
3652898Seric **  PUTMESSAGE -- output a message to the final mailer.
366294Seric **
3672898Seric **	This routine takes care of recreating the header from the
3682898Seric **	in-core copy, etc.
369294Seric **
370294Seric **	Parameters:
3712898Seric **		fp -- file to output onto.
3722898Seric **		m -- a mailer descriptor.
373294Seric **
374294Seric **	Returns:
3752898Seric **		none.
376294Seric **
377294Seric **	Side Effects:
3782898Seric **		The message is written onto fp.
379294Seric */
380294Seric 
3812898Seric putmessage(fp, m)
3822898Seric 	FILE *fp;
3832898Seric 	struct mailer *m;
384294Seric {
3852898Seric 	char buf[BUFSIZ];
3862898Seric 	register int i;
3872898Seric 	HDR *h;
3881828Seric 	register char *p;
3892898Seric 	extern char *arpadate();
3902898Seric 	extern char *hvalue();
3912898Seric 	bool anyheader = FALSE;
3922898Seric 	extern char *translate();
393*3044Seric 	extern char *capitalize();
394294Seric 
3952898Seric 	/* clear all "used" bits */
3962898Seric 	for (h = Header; h != NULL; h = h->h_link)
3972898Seric 		h->h_flags &= ~H_USED;
3982898Seric 
3992898Seric 	/* output date if needed by mailer */
4002898Seric 	p = hvalue("date");
4012968Seric 	if (bitset(M_NEEDDATE, m->m_flags) && p == NULL)
4022898Seric 		p = arpadate(Date);
4032898Seric 	if (p != NULL)
4041828Seric 	{
4052898Seric 		fprintf(fp, "Date: %s\n", p);
4062898Seric 		anyheader = TRUE;
4071828Seric 	}
4081828Seric 
4092898Seric 	/* output from line if needed by mailer */
4102898Seric 	p = hvalue("from");
4112968Seric 	if (bitset(M_NEEDFROM, m->m_flags) && p == NULL)
4122898Seric 	{
4132898Seric 		extern char *FullName;
4142898Seric 
415*3044Seric 		p = translate("$f", From.q_mailer, From.q_paddr, (char *) NULL, (char *) NULL);
4162898Seric 		if (FullName != NULL)
4172898Seric 			fprintf(fp, "From: %s <%s>\n", FullName, p);
4182898Seric 		else
4192898Seric 			fprintf(fp, "From: %s\n", p);
4202898Seric 		free(p);
4212898Seric 		anyheader = TRUE;
4222898Seric 	}
4232898Seric 	else if (p != NULL)
4242898Seric 	{
4252898Seric 		fprintf(fp, "From: %s\n", p);
4262898Seric 		anyheader = TRUE;
4272898Seric 	}
4282898Seric 
4292898Seric 	/* output message-id field if needed */
4302898Seric 	p = hvalue("message-id");
4312968Seric 	if (bitset(M_MSGID, m->m_flags) && p == NULL)
4322898Seric 		p = MsgId;
4332898Seric 	if (p != NULL)
4342898Seric 	{
4352898Seric 		fprintf(fp, "Message-Id: %s\n", p);
4362898Seric 		anyheader = TRUE;
4372898Seric 	}
4382898Seric 
4392898Seric 	/* output any other header lines */
4402898Seric 	for (h = Header; h != NULL; h = h->h_link)
4412898Seric 	{
4422968Seric 		if (bitset(H_USED, h->h_flags))
4432898Seric 			continue;
4442898Seric 		fprintf(fp, "%s: %s\n", capitalize(h->h_field), h->h_value);
4452898Seric 		h->h_flags |= H_USED;
4462898Seric 		anyheader = TRUE;
4472898Seric 	}
4482898Seric 
4492898Seric 	if (anyheader)
4502898Seric 		fprintf(fp, "\n");
4512898Seric 
4522898Seric 	/* output the body of the message */
4532898Seric 	while (!ferror(fp) && (i = read(0, buf, BUFSIZ)) > 0)
4542898Seric 		fwrite(buf, 1, i, fp);
4552898Seric 
456294Seric 	if (ferror(fp))
457294Seric 	{
4582898Seric 		syserr("putmessage: write error");
459294Seric 		setstat(EX_IOERR);
460294Seric 	}
461294Seric }
462294Seric /*
463294Seric **  PIPESIG -- Handle broken pipe signals
464294Seric **
465294Seric **	This just logs an error.
466294Seric **
467294Seric **	Parameters:
468294Seric **		none
469294Seric **
470294Seric **	Returns:
471294Seric **		none
472294Seric **
473294Seric **	Side Effects:
474294Seric **		logs an error message.
475294Seric */
476294Seric 
477294Seric pipesig()
478294Seric {
479294Seric 	syserr("Broken pipe");
4801621Seric 	signal(SIGPIPE, SIG_IGN);
481294Seric }
482294Seric /*
483294Seric **  SENDTO -- Designate a send list.
484294Seric **
485294Seric **	The parameter is a comma-separated list of people to send to.
486294Seric **	This routine arranges to send to all of them.
487294Seric **
488294Seric **	Parameters:
489294Seric **		list -- the send list.
490294Seric **		copyf -- the copy flag; passed to parse.
491294Seric **
492294Seric **	Returns:
493294Seric **		none
494294Seric **
495294Seric **	Side Effects:
496294Seric **		none.
497294Seric **
498294Seric **	Called By:
499294Seric **		main
500294Seric **		alias
501294Seric */
502294Seric 
503294Seric sendto(list, copyf)
504294Seric 	char *list;
505294Seric 	int copyf;
506294Seric {
507294Seric 	register char *p;
508294Seric 	register char *q;
509294Seric 	register char c;
5102968Seric 	ADDRESS *a;
5112968Seric 	extern ADDRESS *parse();
512294Seric 	bool more;
513294Seric 
514294Seric 	/* more keeps track of what the previous delimiter was */
515294Seric 	more = TRUE;
516294Seric 	for (p = list; more; )
517294Seric 	{
518294Seric 		/* find the end of this address */
519294Seric 		q = p;
520294Seric 		while ((c = *p++) != '\0' && c != ',' && c != '\n')
521294Seric 			continue;
522294Seric 		more = c != '\0';
523294Seric 		*--p = '\0';
524294Seric 		if (more)
525294Seric 			p++;
526294Seric 
527294Seric 		/* parse the address */
5282968Seric 		if ((a = parse(q, (ADDRESS *) NULL, copyf)) == NULL)
529294Seric 			continue;
530294Seric 
531294Seric 		/* arrange to send to this person */
532294Seric 		recipient(a, &SendQ);
533294Seric 	}
534294Seric 	To = NULL;
535294Seric }
536294Seric /*
537294Seric **  RECIPIENT -- Designate a message recipient
538294Seric **
539294Seric **	Saves the named person for future mailing.
540294Seric **
541294Seric **	Designates a person as a recipient.  This routine
542294Seric **	does the initial parsing, and checks to see if
543294Seric **	this person has already received the mail.
544294Seric **	It also supresses local network names and turns them into
545294Seric **	local names.
546294Seric **
547294Seric **	Parameters:
548294Seric **		a -- the (preparsed) address header for the recipient.
549294Seric **		targetq -- the queue to add the name to.
550294Seric **
551294Seric **	Returns:
552294Seric **		none.
553294Seric **
554294Seric **	Side Effects:
555294Seric **		none.
556294Seric **
557294Seric **	Called By:
558294Seric **		sendto
559294Seric **		main
560294Seric */
561294Seric 
562294Seric recipient(a, targetq)
5632968Seric 	register ADDRESS *a;
5642968Seric 	ADDRESS *targetq;
565294Seric {
5662968Seric 	register ADDRESS *q;
567294Seric 	register struct mailer *m;
568294Seric 	extern bool forward();
569294Seric 	extern int errno;
570294Seric 	extern bool sameaddr();
571294Seric 
572294Seric 	To = a->q_paddr;
573294Seric 	m = a->q_mailer;
574294Seric 	errno = 0;
575294Seric # ifdef DEBUG
576294Seric 	if (Debug)
577294Seric 		printf("recipient(%s)\n", To);
578294Seric # endif DEBUG
579294Seric 
580294Seric 	/*
581294Seric 	**  Look up this person in the recipient list.  If they
582294Seric 	**  are there already, return, otherwise continue.
583294Seric 	*/
584294Seric 
585294Seric 	if (!ForceMail)
586294Seric 	{
587294Seric 		for (q = &SendQ; (q = nxtinq(q)) != NULL; )
588294Seric 			if (sameaddr(q, a, FALSE))
589294Seric 			{
590294Seric # ifdef DEBUG
591294Seric 				if (Debug)
592294Seric 					printf("(%s in SendQ)\n", a->q_paddr);
593294Seric # endif DEBUG
594294Seric 				return;
595294Seric 			}
596294Seric 		for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
597294Seric 			if (sameaddr(q, a, FALSE))
598294Seric 			{
599294Seric # ifdef DEBUG
600294Seric 				if (Debug)
601294Seric 					printf("(%s in AliasQ)\n", a->q_paddr);
602294Seric # endif DEBUG
603294Seric 				return;
604294Seric 			}
605294Seric 	}
606294Seric 
607294Seric 	/*
608294Seric 	**  See if the user wants hir mail forwarded.
609294Seric 	**	`Forward' must do the forwarding recursively.
610294Seric 	*/
611294Seric 
612294Seric 	if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
613294Seric 		return;
614294Seric 
615294Seric 	/*
616294Seric 	**  Put the user onto the target queue.
617294Seric 	*/
618294Seric 
619294Seric 	if (targetq != NULL)
620294Seric 	{
621294Seric 		putonq(a, targetq);
622294Seric 	}
623294Seric 
624294Seric 	return;
625294Seric }
626294Seric /*
627294Seric **  BUILDARGV -- Build an argument vector for a mail server.
628294Seric **
629294Seric **	Using a template defined in config.c, an argv is built.
630294Seric **	The format of the template is already a vector.  The
631294Seric **	items of this vector are copied, unless a dollar sign
632294Seric **	is encountered.  In this case, the next character
633294Seric **	specifies something else to copy in.  These can be
634294Seric **		$f	The from address.
635294Seric **		$h	The host.
636294Seric **		$u	The user.
637294Seric **		$c	The hop count.
638294Seric **	The vector is built in a local buffer.  A pointer to
639294Seric **	the static argv is returned.
640294Seric **
641294Seric **	Parameters:
6422898Seric **		m -- a pointer to the mailer descriptor.
643294Seric **		host -- the host name to send to.
644294Seric **		user -- the user name to send to.
645294Seric **		from -- the person this mail is from.
646294Seric **
647294Seric **	Returns:
648294Seric **		A pointer to an argv.
649294Seric **
650294Seric **	Side Effects:
651294Seric **		none
652294Seric **
653294Seric **	WARNING:
654294Seric **		Since the argv is staticly allocated, any subsequent
655294Seric **		calls will clobber the old argv.
656294Seric **
657294Seric **	Called By:
658294Seric **		deliver
659294Seric */
660294Seric 
661294Seric char **
6622898Seric buildargv(m, host, user, from)
6632898Seric 	struct mailer *m;
664294Seric 	char *host;
665294Seric 	char *user;
666294Seric 	char *from;
667294Seric {
668294Seric 	register char *p;
669294Seric 	static char *pv[MAXPV+1];
670294Seric 	char **pvp;
671294Seric 	char **mvp;
672294Seric 	static char buf[512];
6732898Seric 	extern char *translate();
674294Seric 
675294Seric 	/*
676294Seric 	**  Do initial argv setup.
677294Seric 	**	Insert the mailer name.  Notice that $x expansion is
678294Seric 	**	NOT done on the mailer name.  Then, if the mailer has
679294Seric 	**	a picky -f flag, we insert it as appropriate.  This
680294Seric 	**	code does not check for 'pv' overflow; this places a
681294Seric 	**	manifest lower limit of 4 for MAXPV.
682294Seric 	*/
683294Seric 
684294Seric 	pvp = pv;
6852898Seric 	*pvp++ = m->m_argv[0];
686294Seric 
687294Seric 	/* insert -f or -r flag as appropriate */
6882968Seric 	if (bitset(M_FOPT|M_ROPT, m->m_flags) && FromFlag)
689294Seric 	{
6902968Seric 		if (bitset(M_FOPT, m->m_flags))
691294Seric 			*pvp++ = "-f";
692294Seric 		else
693294Seric 			*pvp++ = "-r";
6942898Seric 		*pvp++ = translate(from, m, from, user, host);
695294Seric 	}
696294Seric 
697294Seric 	/*
698294Seric 	**  Build the rest of argv.
699294Seric 	**	For each prototype parameter, the prototype is
700294Seric 	**	scanned character at a time.  If a dollar-sign is
701294Seric 	**	found, 'q' is set to the appropriate expansion,
702294Seric 	**	otherwise it is null.  Then either the string
703294Seric 	**	pointed to by q, or the original character, is
704294Seric 	**	interpolated into the buffer.  Buffer overflow is
705294Seric 	**	checked.
706294Seric 	*/
707294Seric 
7082898Seric 	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
709294Seric 	{
710294Seric 		if (pvp >= &pv[MAXPV])
711294Seric 		{
712294Seric 			syserr("Too many parameters to %s", pv[0]);
713294Seric 			return (NULL);
714294Seric 		}
7152898Seric 		*pvp++ = translate(p, m, from, user, host);
7162898Seric 	}
7172898Seric 	*pvp = NULL;
7182898Seric 
7192898Seric # ifdef DEBUG
7202898Seric 	if (Debug)
7212898Seric 	{
7222898Seric 		printf("Interpolated argv is:\n");
7232898Seric 		for (mvp = pv; *mvp != NULL; mvp++)
7242898Seric 			printf("\t%s\n", *mvp);
7252898Seric 	}
7262898Seric # endif DEBUG
7272898Seric 
7282898Seric 	return (pv);
7292898Seric }
7302898Seric /*
7312898Seric **  TRANSLATE -- translate a string using $x escapes.
7322898Seric **
7332898Seric **	Parameters:
7342898Seric **		s -- string to translate.
7352898Seric **		m -- pointer to mailer descriptor.
7362898Seric **
7372898Seric **	Returns:
7382898Seric **		pointer to translated string.
7392898Seric **
7402898Seric **	Side Effects:
7412898Seric **		none.
7422898Seric */
7432898Seric 
7442898Seric char *
7452898Seric translate(s, m, from, user, host)
7462898Seric 	register char *s;
7472898Seric 	struct mailer *m;
7482898Seric 	char *from;
7492898Seric 	char *user;
7502898Seric 	char *host;
7512898Seric {
7522898Seric 	register char *q;
7532898Seric 	char buf[MAXNAME];
7542898Seric 	register char *bp;
7552898Seric 	char *stack = NULL;
7562898Seric 	char pbuf[10];
7572898Seric 	extern char *newstr();
7582898Seric 	extern char *Macro[];
759*3044Seric 	extern char *sprintf();
7602898Seric 
7612898Seric 	bp = buf;
7622898Seric restart:
7632898Seric 
7642898Seric # ifdef DEBUG
7652898Seric 	if (Debug)
7662898Seric 		printf("translate(%s)\n", s);
7672898Seric # endif DEBUG
7682898Seric 	for (; *s != '\0'; s++)
7692898Seric 	{
7702898Seric 		/* q will be the interpolated quantity */
7712898Seric 		q = NULL;
7722898Seric 		if (*s == '$')
773294Seric 		{
7742898Seric 			if (isupper(*++s))
7752898Seric 				q = Macro[*s - 'A'];
7762898Seric 			else
777294Seric 			{
7782898Seric 				switch (*s)
779294Seric 				{
780294Seric 				  case 'f':	/* from person */
7812898Seric 					if (stack == NULL && m != NULL)
7822898Seric 					{
7832898Seric 						stack = s;
7842898Seric 						s = m->m_from;
7852898Seric 						goto restart;
7862898Seric 					}
787294Seric 					q = from;
788294Seric 					break;
789294Seric 
790294Seric 				  case 'u':	/* user */
791294Seric 					q = user;
792294Seric 					break;
793294Seric 
794294Seric 				  case 'h':	/* host */
795294Seric 					q = host;
796294Seric 					break;
797294Seric 
798294Seric 				  case 'c':	/* hop count */
799294Seric 					sprintf(pbuf, "%d", HopCount);
800294Seric 					q = pbuf;
801294Seric 					break;
802294Seric 				}
803294Seric 			}
8042898Seric 		}
805294Seric 
8062898Seric 		/*
8072898Seric 		**  Interpolate q or output one character
8082898Seric 		**	Strip quote bits as we proceed.....
8092898Seric 		*/
810294Seric 
8112898Seric 		if (q != NULL)
8122898Seric 		{
8132898Seric 			while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
8142898Seric 				continue;
8152898Seric 			bp--;
816294Seric 		}
8172898Seric 		else if (bp < &buf[sizeof buf - 1])
8182898Seric 			*bp++ = *s;
819294Seric 	}
8202898Seric 	if (stack != NULL)
8212898Seric 	{
8222898Seric 		s = stack;
8232898Seric 		s++;
8242898Seric 		stack = NULL;
8252898Seric 		goto restart;
8262898Seric 	}
8272898Seric 	*bp++ = '\0';
8282898Seric 	if (bp >= &buf[sizeof buf - 1])
8292898Seric 		return (NULL);
830294Seric # ifdef DEBUG
831294Seric 	if (Debug)
8322898Seric 		printf("translate ==> '%s'\n", buf);
833294Seric # endif DEBUG
8342898Seric 	return (newstr(buf));
835294Seric }
836294Seric /*
837294Seric **  MAILFILE -- Send a message to a file.
838294Seric **
839294Seric **	Parameters:
840294Seric **		filename -- the name of the file to send to.
841294Seric **
842294Seric **	Returns:
843294Seric **		The exit code associated with the operation.
844294Seric **
845294Seric **	Side Effects:
846294Seric **		none.
847294Seric **
848294Seric **	Called By:
849294Seric **		deliver
850294Seric */
851294Seric 
852294Seric mailfile(filename)
853294Seric 	char *filename;
854294Seric {
855294Seric 	char buf[MAXLINE];
856294Seric 	register FILE *f;
857294Seric 	auto long tim;
858294Seric 	extern char *ctime();
859*3044Seric 	extern long time();
860294Seric 
861294Seric 	f = fopen(filename, "a");
862294Seric 	if (f == NULL)
863294Seric 		return (EX_CANTCREAT);
864294Seric 
865294Seric 	/* output the timestamp */
866294Seric 	time(&tim);
867294Seric 	fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
868294Seric 	rewind(stdin);
869294Seric 	while (fgets(buf, sizeof buf, stdin) != NULL)
870294Seric 	{
871294Seric 		fputs(buf, f);
872294Seric 		if (ferror(f))
873294Seric 		{
874294Seric 			fclose(f);
875294Seric 			return (EX_IOERR);
876294Seric 		}
877294Seric 	}
878294Seric 	fputs("\n", f);
879294Seric 	fclose(f);
880294Seric 	return (EX_OK);
881294Seric }
882