1294Seric # include <stdio.h>
2294Seric # include <pwd.h>
3294Seric # include <signal.h>
4294Seric # include "dlvrmail.h"
5294Seric # ifdef LOG
6294Seric # include <log.h>
7294Seric # endif LOG
8294Seric 
9*2089Seric static char SccsId[] = "@(#)deliver.c	2.4	01/08/81";
10405Seric 
11294Seric /*
12294Seric **  DELIVER -- Deliver a message to a particular address.
13294Seric **
14294Seric **	Algorithm:
15294Seric **		Compute receiving network (i.e., mailer), host, & user.
16294Seric **		If local, see if this is really a program name.
17294Seric **		Build argument for the mailer.
18294Seric **		Create pipe through edit fcn if appropriate.
19294Seric **		Fork.
20294Seric **			Child: call mailer
21294Seric **		Parent: call editfcn if specified.
22294Seric **		Wait for mailer to finish.
23294Seric **		Interpret exit status.
24294Seric **
25294Seric **	Parameters:
26294Seric **		to -- the address to deliver the message to.
27294Seric **		editfcn -- if non-NULL, we want to call this function
28294Seric **			to output the letter (instead of just out-
29294Seric **			putting it raw).
30294Seric **
31294Seric **	Returns:
32294Seric **		zero -- successfully delivered.
33294Seric **		else -- some failure, see ExitStat for more info.
34294Seric **
35294Seric **	Side Effects:
36294Seric **		The standard input is passed off to someone.
37294Seric **
38294Seric **	WARNING:
39294Seric **		The standard input is shared amongst all children,
40294Seric **		including the file pointer.  It is critical that the
41294Seric **		parent waits for the child to finish before forking
42294Seric **		another child.
43294Seric **
44294Seric **	Called By:
45294Seric **		main
46294Seric **		savemail
47294Seric **
48294Seric **	Files:
49570Seric **		standard input -- must be opened to the message to
50294Seric **			deliver.
51294Seric */
52294Seric 
53294Seric deliver(to, editfcn)
54294Seric 	addrq *to;
55294Seric 	int (*editfcn)();
56294Seric {
57294Seric 	register struct mailer *m;
58294Seric 	char *host;
59294Seric 	char *user;
60294Seric 	extern struct passwd *getpwnam();
61294Seric 	char **pvp;
62294Seric 	extern char **buildargv();
63294Seric 	auto int st;
64294Seric 	register int i;
65294Seric 	register char *p;
66294Seric 	int pid;
67294Seric 	int pvect[2];
68294Seric 	extern FILE *fdopen();
69294Seric 	extern int errno;
70294Seric 	FILE *mfile;
71294Seric 	extern putheader();
72294Seric 	extern pipesig();
731824Seric 	extern bool GotHdr;
741828Seric 	extern char *index();
75294Seric 
76294Seric 	/*
77294Seric 	**  Compute receiving mailer, host, and to addreses.
78294Seric 	**	Do some initialization first.  To is the to address
79294Seric 	**	for error messages.
80294Seric 	*/
81294Seric 
82294Seric 	To = to->q_paddr;
83294Seric 	m = to->q_mailer;
84294Seric 	user = to->q_user;
85294Seric 	host = to->q_host;
861518Seric 	Errors = 0;
87294Seric 	errno = 0;
88294Seric # ifdef DEBUG
89294Seric 	if (Debug)
90294Seric 		printf("deliver(%s [%d, `%s', `%s'])\n", To, m, host, user);
91294Seric # endif DEBUG
92294Seric 
93294Seric 	/*
94294Seric 	**  Remove quote bits from user/host.
95294Seric 	*/
96294Seric 
97294Seric 	for (p = user; (*p++ &= 0177) != '\0'; )
98294Seric 		continue;
99294Seric 	if (host != NULL)
100294Seric 		for (p = host; (*p++ &= 0177) != '\0'; )
101294Seric 			continue;
102294Seric 
103294Seric 	/*
104294Seric 	**  Strip quote bits from names if the mailer wants it.
105294Seric 	*/
106294Seric 
107294Seric 	if (flagset(M_STRIPQ, m->m_flags))
108294Seric 	{
109294Seric 		stripquotes(user);
110294Seric 		stripquotes(host);
111294Seric 	}
112294Seric 
113294Seric 	/*
114294Seric 	**  See if this user name is "special".
115294Seric 	**	If the user is a program, diddle with the mailer spec.
116294Seric 	**	If the user name has a slash in it, assume that this
117294Seric 	**		is a file -- send it off without further ado.
118294Seric 	**		Note that this means that editfcn's will not
119294Seric 	**		be applied to the message.
120294Seric 	*/
121294Seric 
122294Seric 	if (m == &Mailer[0])
123294Seric 	{
124294Seric 		if (*user == '|')
125294Seric 		{
126294Seric 			user++;
127294Seric 			m = &Mailer[1];
128294Seric 		}
129294Seric 		else
130294Seric 		{
131294Seric 			if (index(user, '/') != NULL)
132294Seric 			{
133294Seric 				i = mailfile(user);
134294Seric 				giveresponse(i, TRUE, m);
135294Seric 				return (i);
136294Seric 			}
137294Seric 		}
138294Seric 	}
139294Seric 
140294Seric 	/*
1411389Seric 	**  See if the user exists.
1421389Seric 	**	Strictly, this is only needed to print a pretty
1431389Seric 	**	error message.
1441389Seric 	**
1451389Seric 	**	>>>>>>>>>> This clause assumes that the local mailer
1461389Seric 	**	>> NOTE >> cannot do any further aliasing; that
1471389Seric 	**	>>>>>>>>>> function is subsumed by delivermail.
148294Seric 	*/
149294Seric 
150294Seric 	if (m == &Mailer[0])
151294Seric 	{
152294Seric 		if (getpwnam(user) == NULL)
153294Seric 		{
154294Seric 			giveresponse(EX_NOUSER, TRUE, m);
155294Seric 			return (EX_NOUSER);
156294Seric 		}
157294Seric 	}
158294Seric 
159294Seric 	/*
160294Seric 	**  If the mailer wants a From line, insert a new editfcn.
161294Seric 	*/
162294Seric 
1631828Seric 	if (flagset(M_HDR, m->m_flags) && editfcn == NULL && (!GotHdr || flagset(M_FHDR, m->m_flags)))
164294Seric 		editfcn = putheader;
165294Seric 
166294Seric 	/*
167294Seric 	**  Call the mailer.
168294Seric 	**	The argument vector gets built, pipes through 'editfcn'
169294Seric 	**	are created as necessary, and we fork & exec as
170294Seric 	**	appropriate.  In the parent, we call 'editfcn'.
171294Seric 	*/
172294Seric 
173294Seric 	pvp = buildargv(m->m_argv, m->m_flags, host, user, From.q_paddr);
174294Seric 	if (pvp == NULL)
175294Seric 	{
176294Seric 		usrerr("name too long");
177294Seric 		return (-1);
178294Seric 	}
179294Seric 	rewind(stdin);
180294Seric 
181294Seric 	/* create a pipe if we will need one */
182294Seric 	if (editfcn != NULL && pipe(pvect) < 0)
183294Seric 	{
184294Seric 		syserr("pipe");
185294Seric 		return (-1);
186294Seric 	}
1871504Smark # ifdef VFORK
1881504Smark 	pid = vfork();
1891504Smark # else
190294Seric 	pid = fork();
1911504Smark # endif
192294Seric 	if (pid < 0)
193294Seric 	{
194294Seric 		syserr("Cannot fork");
195294Seric 		if (editfcn != NULL)
196294Seric 		{
197294Seric 			close(pvect[0]);
198294Seric 			close(pvect[1]);
199294Seric 		}
200294Seric 		return (-1);
201294Seric 	}
202294Seric 	else if (pid == 0)
203294Seric 	{
204294Seric 		/* child -- set up input & exec mailer */
2051621Seric 		/* make diagnostic output be standard output */
2061621Seric 		close(2);
2071621Seric 		dup(1);
208294Seric 		signal(SIGINT, SIG_IGN);
209294Seric 		if (editfcn != NULL)
210294Seric 		{
211294Seric 			close(0);
212294Seric 			if (dup(pvect[0]) < 0)
213294Seric 			{
214294Seric 				syserr("Cannot dup to zero!");
2151619Seric 				_exit(EX_OSERR);
216294Seric 			}
217294Seric 			close(pvect[0]);
218294Seric 			close(pvect[1]);
219294Seric 		}
220294Seric 		if (!flagset(M_RESTR, m->m_flags))
221294Seric 			setuid(getuid());
222294Seric # ifdef LOG
223*2089Seric 		closelog();
224294Seric # endif LOG
2251504Smark # ifndef VFORK
2261504Smark 		/*
2271504Smark 		 * We have to be careful with vfork - we can't mung up the
2281504Smark 		 * memory but we don't want the mailer to inherit any extra
2291504Smark 		 * open files.  Chances are the mailer won't
2301504Smark 		 * care about an extra file, but then again you never know.
2311504Smark 		 * Actually, we would like to close(fileno(pwf)), but it's
2321504Smark 		 * declared static so we can't.  But if we fclose(pwf), which
2331504Smark 		 * is what endpwent does, it closes it in the parent too and
2341504Smark 		 * the next getpwnam will be slower.  If you have a weird mailer
2351504Smark 		 * that chokes on the extra file you should do the endpwent().
2361504Smark 		 */
237294Seric 		endpwent();
2381504Smark # endif
239294Seric 		execv(m->m_mailer, pvp);
240294Seric 		/* syserr fails because log is closed */
241294Seric 		/* syserr("Cannot exec %s", m->m_mailer); */
2421619Seric 		_exit(EX_UNAVAILABLE);
243294Seric 	}
244294Seric 
245294Seric 	/* arrange to write out header message if error */
246294Seric 	if (editfcn != NULL)
247294Seric 	{
248294Seric 		close(pvect[0]);
249294Seric 		signal(SIGPIPE, pipesig);
250294Seric 		mfile = fdopen(pvect[1], "w");
251294Seric 		(*editfcn)(mfile);
252294Seric 		fclose(mfile);
253294Seric 	}
254294Seric 
255294Seric 	/*
256294Seric 	**  Wait for child to die and report status.
257294Seric 	**	We should never get fatal errors (e.g., segmentation
258294Seric 	**	violation), so we report those specially.  For other
259294Seric 	**	errors, we choose a status message (into statmsg),
260294Seric 	**	and if it represents an error, we print it.
261294Seric 	*/
262294Seric 
263294Seric 	while ((i = wait(&st)) > 0 && i != pid)
264294Seric 		continue;
265294Seric 	if (i < 0)
266294Seric 	{
267294Seric 		syserr("wait");
268294Seric 		return (-1);
269294Seric 	}
270294Seric 	if ((st & 0377) != 0)
271294Seric 	{
272294Seric 		syserr("%s: stat %o", pvp[0], st);
2731597Seric 		ExitStat = EX_UNAVAILABLE;
274294Seric 		return (-1);
275294Seric 	}
276294Seric 	i = (st >> 8) & 0377;
277294Seric 	giveresponse(i, FALSE, m);
278294Seric 	return (i);
279294Seric }
280294Seric /*
281294Seric **  GIVERESPONSE -- Interpret an error response from a mailer
282294Seric **
283294Seric **	Parameters:
284294Seric **		stat -- the status code from the mailer (high byte
285294Seric **			only; core dumps must have been taken care of
286294Seric **			already).
287294Seric **		force -- if set, force an error message output, even
288294Seric **			if the mailer seems to like to print its own
289294Seric **			messages.
290294Seric **		m -- the mailer descriptor for this mailer.
291294Seric **
292294Seric **	Returns:
293294Seric **		none.
294294Seric **
295294Seric **	Side Effects:
2961518Seric **		Errors may be incremented.
297294Seric **		ExitStat may be set.
298294Seric **
299294Seric **	Called By:
300294Seric **		deliver
301294Seric */
302294Seric 
303294Seric giveresponse(stat, force, m)
304294Seric 	int stat;
305294Seric 	int force;
306294Seric 	register struct mailer *m;
307294Seric {
308294Seric 	register char *statmsg;
309294Seric 	extern char *SysExMsg[];
310294Seric 	register int i;
311294Seric 	extern int N_SysEx;
3121624Seric 	extern long MsgSize;
3131624Seric 	char buf[30];
314294Seric 
315294Seric 	i = stat - EX__BASE;
316294Seric 	if (i < 0 || i > N_SysEx)
317294Seric 		statmsg = NULL;
318294Seric 	else
319294Seric 		statmsg = SysExMsg[i];
320294Seric 	if (stat == 0)
321294Seric 		statmsg = "ok";
322294Seric 	else
323294Seric 	{
3241518Seric 		Errors++;
325294Seric 		if (statmsg == NULL && m->m_badstat != 0)
326294Seric 		{
327294Seric 			stat = m->m_badstat;
328294Seric 			i = stat - EX__BASE;
329294Seric # ifdef DEBUG
330294Seric 			if (i < 0 || i >= N_SysEx)
331294Seric 				syserr("Bad m_badstat %d", stat);
332294Seric 			else
333294Seric # endif DEBUG
334294Seric 			statmsg = SysExMsg[i];
335294Seric 		}
336294Seric 		if (statmsg == NULL)
337294Seric 			usrerr("unknown mailer response %d", stat);
338294Seric 		else if (force || !flagset(M_QUIET, m->m_flags))
339294Seric 			usrerr("%s", statmsg);
340294Seric 	}
341294Seric 
342294Seric 	/*
343294Seric 	**  Final cleanup.
344294Seric 	**	Log a record of the transaction.  Compute the new
345294Seric 	**	ExitStat -- if we already had an error, stick with
346294Seric 	**	that.
347294Seric 	*/
348294Seric 
3491624Seric 	if (statmsg == NULL)
3501624Seric 	{
3511624Seric 		sprintf(buf, "error %d", stat);
3521624Seric 		statmsg = buf;
3531624Seric 	}
3541624Seric 
355294Seric # ifdef LOG
3561624Seric 	logmsg(LOG_INFO, "%s->%s: %ld: %s", From.q_paddr, To, MsgSize, statmsg);
357294Seric # endif LOG
3581389Seric 	setstat(stat);
359294Seric 	return (stat);
360294Seric }
361294Seric /*
362294Seric **  PUTHEADER -- insert the From header into some mail
363294Seric **
364294Seric **	For mailers such as 'msgs' that want the header inserted
365294Seric **	into the mail, this edit filter inserts the From line and
3661824Seric **	then passes the rest of the message through.
367294Seric **
368294Seric **	Parameters:
369294Seric **		fp -- the file pointer for the output.
370294Seric **
371294Seric **	Returns:
372294Seric **		none
373294Seric **
374294Seric **	Side Effects:
375294Seric **		Puts a "From" line in UNIX format, and then
376294Seric **			outputs the rest of the message.
377294Seric **
378294Seric **	Called By:
379294Seric **		deliver
380294Seric */
381294Seric 
382294Seric putheader(fp)
383294Seric 	register FILE *fp;
384294Seric {
385294Seric 	char buf[MAXLINE + 1];
386294Seric 	long tim;
387294Seric 	extern char *ctime();
3881828Seric 	register char *p;
3891828Seric 	extern char *index();
390294Seric 
3911828Seric 	/* output the header part */
3921828Seric 	fgets(buf, sizeof buf, stdin);
3931828Seric 	if (strncmp(buf, "From ", 5) != 0 || (p = index(&buf[5], ' ')) == NULL)
3941828Seric 	{
3951828Seric 		time(&tim);
3961828Seric 		fprintf(fp, "From %s %s", From.q_paddr, ctime(&tim));
397294Seric 		fputs(buf, fp);
3981828Seric 	}
3991828Seric 	else
4001828Seric 		fprintf(fp, "From %s %s", From.q_paddr, &p[1]);
4011828Seric 
4021828Seric 	/* output the body */
4031828Seric 	while (!ferror(fp) && fgets(buf, sizeof buf, stdin) != NULL)
4041828Seric 		fputs(buf, fp);
405294Seric 	if (ferror(fp))
406294Seric 	{
407294Seric 		syserr("putheader: write error");
408294Seric 		setstat(EX_IOERR);
409294Seric 	}
410294Seric }
411294Seric /*
412294Seric **  PIPESIG -- Handle broken pipe signals
413294Seric **
414294Seric **	This just logs an error.
415294Seric **
416294Seric **	Parameters:
417294Seric **		none
418294Seric **
419294Seric **	Returns:
420294Seric **		none
421294Seric **
422294Seric **	Side Effects:
423294Seric **		logs an error message.
424294Seric */
425294Seric 
426294Seric pipesig()
427294Seric {
428294Seric 	syserr("Broken pipe");
4291621Seric 	signal(SIGPIPE, SIG_IGN);
430294Seric }
431294Seric /*
432294Seric **  SENDTO -- Designate a send list.
433294Seric **
434294Seric **	The parameter is a comma-separated list of people to send to.
435294Seric **	This routine arranges to send to all of them.
436294Seric **
437294Seric **	Parameters:
438294Seric **		list -- the send list.
439294Seric **		copyf -- the copy flag; passed to parse.
440294Seric **
441294Seric **	Returns:
442294Seric **		none
443294Seric **
444294Seric **	Side Effects:
445294Seric **		none.
446294Seric **
447294Seric **	Called By:
448294Seric **		main
449294Seric **		alias
450294Seric */
451294Seric 
452294Seric sendto(list, copyf)
453294Seric 	char *list;
454294Seric 	int copyf;
455294Seric {
456294Seric 	register char *p;
457294Seric 	register char *q;
458294Seric 	register char c;
459294Seric 	addrq *a;
460294Seric 	extern addrq *parse();
461294Seric 	bool more;
462294Seric 
463294Seric 	/* more keeps track of what the previous delimiter was */
464294Seric 	more = TRUE;
465294Seric 	for (p = list; more; )
466294Seric 	{
467294Seric 		/* find the end of this address */
468294Seric 		q = p;
469294Seric 		while ((c = *p++) != '\0' && c != ',' && c != '\n')
470294Seric 			continue;
471294Seric 		more = c != '\0';
472294Seric 		*--p = '\0';
473294Seric 		if (more)
474294Seric 			p++;
475294Seric 
476294Seric 		/* parse the address */
477294Seric 		if ((a = parse(q, (addrq *) NULL, copyf)) == NULL)
478294Seric 			continue;
479294Seric 
480294Seric 		/* arrange to send to this person */
481294Seric 		recipient(a, &SendQ);
482294Seric 	}
483294Seric 	To = NULL;
484294Seric }
485294Seric /*
486294Seric **  RECIPIENT -- Designate a message recipient
487294Seric **
488294Seric **	Saves the named person for future mailing.
489294Seric **
490294Seric **	Designates a person as a recipient.  This routine
491294Seric **	does the initial parsing, and checks to see if
492294Seric **	this person has already received the mail.
493294Seric **	It also supresses local network names and turns them into
494294Seric **	local names.
495294Seric **
496294Seric **	Parameters:
497294Seric **		a -- the (preparsed) address header for the recipient.
498294Seric **		targetq -- the queue to add the name to.
499294Seric **
500294Seric **	Returns:
501294Seric **		none.
502294Seric **
503294Seric **	Side Effects:
504294Seric **		none.
505294Seric **
506294Seric **	Called By:
507294Seric **		sendto
508294Seric **		main
509294Seric */
510294Seric 
511294Seric recipient(a, targetq)
512294Seric 	register addrq *a;
513294Seric 	addrq *targetq;
514294Seric {
515294Seric 	register addrq *q;
516294Seric 	register struct mailer *m;
517294Seric 	register char **pvp;
518294Seric 	extern char *xalloc();
519294Seric 	extern bool forward();
520294Seric 	extern int errno;
521294Seric 	extern bool sameaddr();
522294Seric 
523294Seric 	To = a->q_paddr;
524294Seric 	m = a->q_mailer;
525294Seric 	errno = 0;
526294Seric # ifdef DEBUG
527294Seric 	if (Debug)
528294Seric 		printf("recipient(%s)\n", To);
529294Seric # endif DEBUG
530294Seric 
531294Seric 	/*
532294Seric 	**  Look up this person in the recipient list.  If they
533294Seric 	**  are there already, return, otherwise continue.
534294Seric 	*/
535294Seric 
536294Seric 	if (!ForceMail)
537294Seric 	{
538294Seric 		for (q = &SendQ; (q = nxtinq(q)) != NULL; )
539294Seric 			if (sameaddr(q, a, FALSE))
540294Seric 			{
541294Seric # ifdef DEBUG
542294Seric 				if (Debug)
543294Seric 					printf("(%s in SendQ)\n", a->q_paddr);
544294Seric # endif DEBUG
545294Seric 				return;
546294Seric 			}
547294Seric 		for (q = &AliasQ; (q = nxtinq(q)) != NULL; )
548294Seric 			if (sameaddr(q, a, FALSE))
549294Seric 			{
550294Seric # ifdef DEBUG
551294Seric 				if (Debug)
552294Seric 					printf("(%s in AliasQ)\n", a->q_paddr);
553294Seric # endif DEBUG
554294Seric 				return;
555294Seric 			}
556294Seric 	}
557294Seric 
558294Seric 	/*
559294Seric 	**  See if the user wants hir mail forwarded.
560294Seric 	**	`Forward' must do the forwarding recursively.
561294Seric 	*/
562294Seric 
563294Seric 	if (m == &Mailer[0] && !NoAlias && targetq == &SendQ && forward(a))
564294Seric 		return;
565294Seric 
566294Seric 	/*
567294Seric 	**  Put the user onto the target queue.
568294Seric 	*/
569294Seric 
570294Seric 	if (targetq != NULL)
571294Seric 	{
572294Seric 		putonq(a, targetq);
573294Seric 	}
574294Seric 
575294Seric 	return;
576294Seric }
577294Seric /*
578294Seric **  BUILDARGV -- Build an argument vector for a mail server.
579294Seric **
580294Seric **	Using a template defined in config.c, an argv is built.
581294Seric **	The format of the template is already a vector.  The
582294Seric **	items of this vector are copied, unless a dollar sign
583294Seric **	is encountered.  In this case, the next character
584294Seric **	specifies something else to copy in.  These can be
585294Seric **		$f	The from address.
586294Seric **		$h	The host.
587294Seric **		$u	The user.
588294Seric **		$c	The hop count.
589294Seric **	The vector is built in a local buffer.  A pointer to
590294Seric **	the static argv is returned.
591294Seric **
592294Seric **	Parameters:
593294Seric **		tmplt -- a template for an argument vector.
594294Seric **		flags -- the flags for this server.
595294Seric **		host -- the host name to send to.
596294Seric **		user -- the user name to send to.
597294Seric **		from -- the person this mail is from.
598294Seric **
599294Seric **	Returns:
600294Seric **		A pointer to an argv.
601294Seric **
602294Seric **	Side Effects:
603294Seric **		none
604294Seric **
605294Seric **	WARNING:
606294Seric **		Since the argv is staticly allocated, any subsequent
607294Seric **		calls will clobber the old argv.
608294Seric **
609294Seric **	Called By:
610294Seric **		deliver
611294Seric */
612294Seric 
613294Seric char **
614294Seric buildargv(tmplt, flags, host, user, from)
615294Seric 	char **tmplt;
616294Seric 	int flags;
617294Seric 	char *host;
618294Seric 	char *user;
619294Seric 	char *from;
620294Seric {
621294Seric 	register char *p;
622294Seric 	register char *q;
623294Seric 	static char *pv[MAXPV+1];
624294Seric 	char **pvp;
625294Seric 	char **mvp;
626294Seric 	static char buf[512];
627294Seric 	register char *bp;
628294Seric 	char pbuf[30];
629294Seric 
630294Seric 	/*
631294Seric 	**  Do initial argv setup.
632294Seric 	**	Insert the mailer name.  Notice that $x expansion is
633294Seric 	**	NOT done on the mailer name.  Then, if the mailer has
634294Seric 	**	a picky -f flag, we insert it as appropriate.  This
635294Seric 	**	code does not check for 'pv' overflow; this places a
636294Seric 	**	manifest lower limit of 4 for MAXPV.
637294Seric 	*/
638294Seric 
639294Seric 	pvp = pv;
640294Seric 	bp = buf;
641294Seric 
642294Seric 	*pvp++ = tmplt[0];
643294Seric 
644294Seric 	/* insert -f or -r flag as appropriate */
645294Seric 	if (flagset(M_FOPT|M_ROPT, flags) && FromFlag)
646294Seric 	{
647294Seric 		if (flagset(M_FOPT, flags))
648294Seric 			*pvp++ = "-f";
649294Seric 		else
650294Seric 			*pvp++ = "-r";
651294Seric 		*pvp++ = From.q_paddr;
652294Seric 	}
653294Seric 
654294Seric 	/*
655294Seric 	**  Build the rest of argv.
656294Seric 	**	For each prototype parameter, the prototype is
657294Seric 	**	scanned character at a time.  If a dollar-sign is
658294Seric 	**	found, 'q' is set to the appropriate expansion,
659294Seric 	**	otherwise it is null.  Then either the string
660294Seric 	**	pointed to by q, or the original character, is
661294Seric 	**	interpolated into the buffer.  Buffer overflow is
662294Seric 	**	checked.
663294Seric 	*/
664294Seric 
665294Seric 	for (mvp = tmplt; (p = *++mvp) != NULL; )
666294Seric 	{
667294Seric 		if (pvp >= &pv[MAXPV])
668294Seric 		{
669294Seric 			syserr("Too many parameters to %s", pv[0]);
670294Seric 			return (NULL);
671294Seric 		}
672294Seric 		*pvp++ = bp;
673294Seric 		for (; *p != '\0'; p++)
674294Seric 		{
675294Seric 			/* q will be the interpolated quantity */
676294Seric 			q = NULL;
677294Seric 			if (*p == '$')
678294Seric 			{
679294Seric 				switch (*++p)
680294Seric 				{
681294Seric 				  case 'f':	/* from person */
682294Seric 					q = from;
683294Seric 					break;
684294Seric 
685294Seric 				  case 'u':	/* user */
686294Seric 					q = user;
687294Seric 					break;
688294Seric 
689294Seric 				  case 'h':	/* host */
690294Seric 					q = host;
691294Seric 					break;
692294Seric 
693294Seric 				  case 'c':	/* hop count */
694294Seric 					sprintf(pbuf, "%d", HopCount);
695294Seric 					q = pbuf;
696294Seric 					break;
697294Seric 				}
698294Seric 			}
699294Seric 
700294Seric 			/*
701294Seric 			**  Interpolate q or output one character
702294Seric 			**	Strip quote bits as we proceed.....
703294Seric 			*/
704294Seric 
705294Seric 			if (q != NULL)
706294Seric 			{
707294Seric 				while (bp < &buf[sizeof buf - 1] && (*bp++ = *q++) != '\0')
708294Seric 					continue;
709294Seric 				bp--;
710294Seric 			}
711294Seric 			else if (bp < &buf[sizeof buf - 1])
712294Seric 				*bp++ = *p;
713294Seric 		}
714294Seric 		*bp++ = '\0';
715294Seric 		if (bp >= &buf[sizeof buf - 1])
716294Seric 			return (NULL);
717294Seric 	}
718294Seric 	*pvp = NULL;
719294Seric 
720294Seric # ifdef DEBUG
721294Seric 	if (Debug)
722294Seric 	{
723294Seric 		printf("Interpolated argv is:\n");
724294Seric 		for (mvp = pv; *mvp != NULL; mvp++)
725294Seric 			printf("\t%s\n", *mvp);
726294Seric 	}
727294Seric # endif DEBUG
728294Seric 
729294Seric 	return (pv);
730294Seric }
731294Seric /*
732294Seric **  MAILFILE -- Send a message to a file.
733294Seric **
734294Seric **	Parameters:
735294Seric **		filename -- the name of the file to send to.
736294Seric **
737294Seric **	Returns:
738294Seric **		The exit code associated with the operation.
739294Seric **
740294Seric **	Side Effects:
741294Seric **		none.
742294Seric **
743294Seric **	Called By:
744294Seric **		deliver
745294Seric */
746294Seric 
747294Seric mailfile(filename)
748294Seric 	char *filename;
749294Seric {
750294Seric 	char buf[MAXLINE];
751294Seric 	register FILE *f;
752294Seric 	auto long tim;
753294Seric 	extern char *ctime();
754294Seric 
755294Seric 	f = fopen(filename, "a");
756294Seric 	if (f == NULL)
757294Seric 		return (EX_CANTCREAT);
758294Seric 
759294Seric 	/* output the timestamp */
760294Seric 	time(&tim);
761294Seric 	fprintf(f, "From %s %s", From.q_paddr, ctime(&tim));
762294Seric 	rewind(stdin);
763294Seric 	while (fgets(buf, sizeof buf, stdin) != NULL)
764294Seric 	{
765294Seric 		fputs(buf, f);
766294Seric 		if (ferror(f))
767294Seric 		{
768294Seric 			fclose(f);
769294Seric 			return (EX_IOERR);
770294Seric 		}
771294Seric 	}
772294Seric 	fputs("\n", f);
773294Seric 	fclose(f);
774294Seric 	return (EX_OK);
775294Seric }
776